home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 July: Technology Seed / ADC Seed CD - July 1999.toast / USB / Mac OS USB DDK v1.2 / Examples / USBSampleStorageDriver / StorageClassUTFunctions.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-04-15  |  83.7 KB  |  2,744 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        StorageClassUTFunctions.c
  3.  
  4.     Contains:    All device specific functions
  5.  
  6.     Version:    1.1
  7.  
  8.     Copyright:    © 1998-1999 by Apple Computer, Inc., all rights reserved.
  9.  
  10. */
  11.  
  12. #include <Disks.h>
  13. #include <DriverGestalt.h>
  14. #include <Gestalt.h>
  15. #include <NameRegistry.h>
  16. #include <Power.h>
  17. #include <Resources.h>
  18. #include <Strings.h>
  19.  
  20. #include "SampleStorageDriverAPI.h"
  21. #include "SampleStorageVersion.h"
  22. #include "StorageClassUTFunctions.h"
  23. #include "StorageClassUTDriver.h"
  24. #include "StorageClassUTDriverIcons.h"
  25.  
  26. enum 
  27. {
  28.     kReadWriteRetryCount = 1,
  29.     kFormatRequestSenseRetryCount = 5
  30. };
  31.     
  32. enum {
  33. //    kCDBSize = 12,
  34.     kSenseDataSize = 18
  35. };
  36.  
  37.  
  38. enum {
  39.     kCmdFormat                     = 0x04,
  40.     kCmdInquiry                 = 0x12,
  41.     kCmdModeSelect                 = 0x55,
  42.     kCmdModeSense                 = 0x5A,
  43.     kCmdPreventAllowRemoval     = 0x1E,
  44.     kCmdRead                     = 0x28,
  45.     kCmdReadCapacity             = 0x25,
  46.     kCmdReadFormatCapacities     = 0x23,
  47.     kCmdRequestSense             = 0x03,
  48.     kCmdRezero                     = 0x01,
  49.     kCmdSeek                     = 0x2B,
  50.     kCmdSendDiagnostic             = 0x1D,
  51.     kCmdStartStopUnit             = 0x1B,
  52.     kCmdTestUnitReady             = 0x00,
  53.     kCmdWrite                     = 0x2A,
  54.     kCmdWriteVerify             = 0x2E,
  55.     kCmdVerify                     = 0x2F
  56. };
  57.  
  58. // The structure for getting the media capacity from the device    
  59. struct ReadCapacityData 
  60. {
  61.     UInt32        lastLogicalBlock;        // Last valid LBA
  62.     UInt32        blockLength;            // Block length in bytes
  63. };
  64. typedef struct ReadCapacityData     ReadCapacityData, *ReadCapacityDataPtr;
  65.  
  66. // This structure defines each volume (partition) on a physical drive (media)
  67. struct VolumeRec                        // This is needed to support multiple partitions
  68. {
  69.     DrvSts2            driveStatus;        // drive status info & queue element
  70.     UInt16            vRefNum;            // drive number for this volume
  71.     Boolean            mountthispart;        // mount this volume indicator
  72.     Boolean            partmounted;        // volume mounted indicator
  73.     UInt32            partitionNo;        // the partition number for the volume
  74.     UInt32            partoffset;            // phys. offset of data partition
  75.     UInt32            curoffset;            // 0 = physical mapping, else 'partoffset'
  76.     UInt32            partblks;            // number of blocks in the partition
  77.     Ptr                nextVol;            // Link to next volume in the list
  78.     Ptr                drivePtr;            // Pointer to the owning drive's record
  79.     Ptr                mediaIconPtr;        // Pointer to the media icon for this volume
  80. };
  81. typedef    struct VolumeRec     VolumeRec, *VolumeRecPtr;
  82.  
  83. // This structure defines each physical drive…
  84. struct DriveRec
  85. {
  86.     UInt32            capacity;            // Drive capacity in blocks
  87.     UInt32            blockSize;
  88.     UInt16            numVolumes;            // Number of partitions in the drive
  89.     VolumeRecPtr    nextVol;            // Pointer to drive's volume list    
  90. };
  91. typedef struct DriveRec     DriveRec, *DriveRecPtr;
  92.  
  93. // Our Sleep Queue structure.  The driver installs a sleep queue proc to find
  94. // out when the machine goes into sleep or doze mode.  When a sleep or doze
  95. // notification is received, the driver will stop polling for media until a wake
  96. // notification is received.    
  97. struct OurSleepQRec
  98. {
  99.     SleepQRec    theSleepQRec;
  100.     Boolean        isInSleep;
  101. };
  102. typedef struct OurSleepQRec    OurSleepQRec, *OurSleepQRecPtr;
  103.  
  104. // The Drive Request PB structure contains all information need to perform
  105. // a device request
  106. struct DriveRequestPB
  107. {
  108.     StorageExecuteCommandPB        executePB;
  109.     ParmBlkPtr                    theIOPB;
  110.     IOCommandID                 ioCommandID;
  111.     IOCommandKind                 ioCommandKind;
  112.     volatile OSStatus            status;
  113.     UInt16                        retryCount;
  114.     Boolean                        doWrite;
  115. };
  116. typedef struct DriveRequestPB    DriveRequestPB, *DriveRequestPBPtr;
  117.  
  118. // The driver's global data structure
  119. struct UTDriverGlobals
  120. {
  121.     DriveRequestPB                drivePB;        
  122.  
  123.     // Global Control/Status fields for the driver
  124.     Boolean                        isFloppy;
  125.     Boolean                        isWriteProtected;
  126.     Boolean                        doInternalReadWrite;
  127.     Boolean                        diskInDrive;
  128.     UInt8                        currentExecutionState;
  129.     
  130.     SInt16                        drvrRefNum;            // Our driver reference number
  131.     ReadCapacityData            getCapacity;
  132.     UInt8                        sense[40];
  133.     DriveRec                    theDrive;
  134.     VolumeRec                    theVolume;
  135.     OurSleepQRec                theSleepQRec;
  136.     Str32                        DriveInfoString;
  137. };
  138. typedef struct UTDriverGlobals    UTDriverGlobals;
  139.  
  140. // The values for the states of the state machines
  141. enum
  142. {
  143.     // Mount State Machine
  144.     kMountStartState = 1,
  145.     kMountTURDoneState,
  146.     kMountRequestSenseDoneState,
  147.     kMountGetGeometryDoneState,
  148.     kMountCheckGetGeometryErrorDone,
  149.     kMountReadPossibleCapacitiesDone,
  150.     kMountCheckWriteProtectDoneState,
  151.     kMountPreventRemovalDoneState,
  152.  
  153.     // Eject State Machine
  154.     kEjectStartState,
  155.     kEjectAllowRemovalDone,
  156.     kEjectCartridgeDone,
  157.  
  158.     // Format State Machine
  159.     kFormatStartState,
  160.     kFormatDoneState,
  161.     kFormatWaitDoneState,
  162.     kFormatRequestSenseDoneState,
  163.     kFormatGetGeometryDoneState,
  164.     kFormatCheckWriteProtectDoneState,
  165.     kFormatPreventRemovalDoneState
  166. };
  167.  
  168. /* Driver Control/Status Codes for Startup Disk Control Panel */
  169. enum
  170. {
  171.     kcsSetBootPartitionCode            = 44,        // Set startup partition Control Call
  172.     kcsGetBootPartitionStatus        = 44        // Get startup partition Status Call
  173. };
  174.  
  175. // Prototypes for the Sleep Queue related functions
  176. static void             InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec);    
  177. static void             RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec);    
  178. static long             SleepNotification(long message, SleepQRecPtr sleepRec);
  179.  
  180. // Prototypes for the cartridge mounting related functions
  181. static OSStatus         MountSecondaryInterrupt( void *p1, void *p2);
  182. static void             MountTheCartridge( void *theCurrentPB );
  183.  
  184. // Prototypes for the cartridge ejecting related functions
  185. static void             EjectTheCartridge( void *theCurrentPB );
  186.  
  187. // Prototypes for the floppy formatting related functions
  188. static OSStatus         FormatCompletionInterrupt( void *p1, void *p2);
  189. static void             FormatTheCartridge( void *theCurrentPB );
  190.  
  191. // ATAPI/SCSI-2 Device Commands
  192. static OSStatus         CheckWriteProtect(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  193. static OSStatus         TUR(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  194. static OSStatus         GetMediaGeometry(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  195. static OSStatus         ReadFormatCapacity(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  196. static OSStatus            RequestSense(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  197. static OSStatus         FormatFloppyCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  198. static OSStatus         EjectCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  199. static OSStatus         PreventAllowRemoval(UTDriverGlobals *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion);
  200.  
  201. // Prototypes for the Read/Write functions and completion routines
  202. static OSStatus         DoReadWriteCommand( UTDriverGlobals *theDriverPB, Boolean doWrite);
  203. static OSStatus         ReadWriteBlock( UTDriverGlobals *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync);
  204. static void             ReadWriteCompletion( void *theDriverPB );
  205. static void             WriteRequestSenseCompletion( void *theDriverPB );
  206. static void             RequestSenseOnErrorCompletion( void *theDriverPB );
  207.  
  208. // Prototypes for the Drive Queue and Volume Queue related functions
  209. static void             InstallVolumes(DriveRecPtr theDrive, Boolean mountVols);
  210. static void                RemoveVolume(DriveRecPtr drvRec, UInt16 volRef);
  211. static VolumeRecPtr        GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum);
  212. static VCB*                MountedVolOfDrive(DriveRecPtr drive);
  213. static SInt16            NextQDrive();
  214. static VolumeRecPtr        CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset);
  215. static void             RemoveDrive(DriveRecPtr theDrivePtr);
  216. static void                UpdateQ(SInt16 qDrive, SInt32 newSize);
  217. static SInt32            NextPartitionID(DriveRecPtr drive);
  218. static Boolean             InstallDrive(DriveRecPtr drive, Boolean mountVols);
  219. static OSErr             MountVolumes( DriveRecPtr drive );
  220.  
  221. // This is to workaround a bug in the PowerPC native version of the AddDrive
  222. // call in systems before 8.5, where one needs to be added to the desired
  223. // drive number before calling AddDrive.
  224. // This function will check the system and pass the appropriate value to AddDrive
  225. static void             NativeAddDrive(SInt16 drvrRefNum, UInt16 driveNumber, DrvQElPtr drvQEl);
  226.  
  227. //----------------------------------------------------------------------------------
  228. //                                    Globals
  229. //----------------------------------------------------------------------------------
  230.  
  231. static DriverLocationIcon                gDriveIcon;                            // static structure for control calls
  232. static StorageClassDispatchTablePtr        gItsTheDispatchTable = nil;            // The class's dispatch table
  233. static TimerID                            gInterruptTimer = 0;
  234. static UTDriverGlobals                    gTheUTGlobals;
  235.  
  236.  
  237. //----------------------------------------------------------------------------------
  238. //                                    Driver calls
  239. //----------------------------------------------------------------------------------
  240.  
  241. //    Always run at task level, can allocate and move memory.
  242. OSStatus DriverInitializeCmd (    AddressSpaceID        addressSpaceID,
  243.                                 DriverInitInfoPtr    initialInfo)
  244. {
  245. #pragma unused (addressSpaceID, initialInfo)
  246.     
  247.     OSStatus    err = noErr;
  248.  
  249.     IfDebugging("\pInitialize Driver");    
  250.     gTheUTGlobals.drvrRefNum = initialInfo->refNum;
  251.     gTheUTGlobals.drivePB.theIOPB = nil;
  252.     gTheUTGlobals.currentExecutionState = kMountStartState;
  253.     gTheUTGlobals.doInternalReadWrite = false;
  254.     gTheUTGlobals.diskInDrive = false;
  255.     
  256.     gTheUTGlobals.isFloppy = false;
  257.  
  258.     // Set up our color icon family
  259.     BuildMediaIconFamily();
  260.  
  261.     // Clear out the Drive Record
  262.     BlockZero((Ptr) &gTheUTGlobals.theDrive, sizeof(DriveRec));
  263.     
  264.     // Clear out the Volume Record
  265.     BlockZero((Ptr) &gTheUTGlobals.theVolume, sizeof(VolumeRec));
  266.     
  267.     // Build the Drive Info string returned in Status/Control calls
  268.     {
  269.         Str32 tempStr;
  270.         
  271.         PStrCopy( gTheUTGlobals.DriveInfoString, "\pUSB (v");
  272.         CStrToPStr(tempStr, kStorageStringVersShort); // driver version
  273.         PStrCat(gTheUTGlobals.DriveInfoString, tempStr); // driver version
  274.         PStrCat(gTheUTGlobals.DriveInfoString, "\p)");
  275.     }
  276.  
  277.     BlockZero((Ptr) &gTheUTGlobals.theSleepQRec, sizeof(OurSleepQRec));
  278.     InstallInSleepQueue( &gTheUTGlobals.theSleepQRec );
  279.     return (err);
  280. }
  281.  
  282. //    Always run at task level, can allocate and move memory.
  283. OSStatus DriverFinalizeCmd (DriverFinalInfoPtr finalInfo)
  284. {
  285. #pragma unused (finalInfo)
  286.     OSStatus    err = noErr;
  287.  
  288.     DestroyMediaIconFamily();
  289.     RemoveFromSleepQueue( &gTheUTGlobals.theSleepQRec );    
  290.     
  291.     return (err);
  292. }
  293.  
  294. //    Always run at task level, can allocate and move memory.
  295. OSStatus DriverSupersededCmd (DriverSupersededInfoPtr supersededInfo)
  296. {
  297. #pragma unused (supersededInfo)
  298.  
  299.     OSErr    err = noErr;
  300.  
  301.     return (err);
  302. }
  303.  
  304. //    Always run at task level, can allocate and move memory.
  305. OSStatus DriverReplaceCmd (    AddressSpaceID            addressSpaceID,
  306.                             DriverReplaceInfoPtr    replaceInfo)
  307. {
  308. #pragma unused (addressSpaceID, replaceInfo)
  309.     OSStatus    err = noErr;
  310.  
  311.     return (err);
  312. }
  313.  
  314. //    Always run at task level, can allocate and move memory.
  315. OSStatus DriverOpenCmd ( AddressSpaceID    addressSpaceID, ParmBlkPtr pb)
  316. {
  317. #pragma unused (addressSpaceID, pb)
  318.     OSStatus    err         = noErr;
  319.  
  320.     // Let the world know we can do DriverGestalt calls
  321.     DriverGestaltOn(gTheUTGlobals.drvrRefNum);
  322.     
  323.     return(err);
  324. }
  325.  
  326. //    Always run at task level, can allocate and move memory.
  327. OSStatus DriverCloseCmd (ParmBlkPtr pb)
  328. {
  329. #pragma unused (pb)
  330.     OSStatus        err = noErr;
  331.  
  332.     gTheUTGlobals.diskInDrive = false;
  333.  
  334.     // If an interrupt timer is set, cancel it!
  335.     if( gInterruptTimer != 0 )
  336.     {
  337.         AbsoluteTime    timeLeft;
  338.         
  339.         // Cancel any pending timers
  340.         CancelTimer( gInterruptTimer, &timeLeft);
  341.         gInterruptTimer = 0;
  342.     }
  343.  
  344.     // If a command is pending, we should cancel it.
  345.     // Since we currently have no way of informing the Class driver to abort,
  346.     // we will wait for the class driver to tell us the command has finished.
  347.     while(    gTheUTGlobals.drivePB.theIOPB != nil );
  348.     
  349.     // Dequeue all drive volumes 
  350.     RemoveDrive(&gTheUTGlobals.theDrive);
  351.     
  352.     if(gItsTheDispatchTable != nil)
  353.     {
  354.         (void) ((gItsTheDispatchTable)->pStorageControl)(kControlEnableRemoval, nil);
  355.     }
  356.     
  357.     // Let the world know we can no longer doDriverGestalt calls
  358.     DriverGestaltOff(gTheUTGlobals.drvrRefNum);
  359.     
  360.     return (err);
  361. }
  362.  
  363. //    May run at interrupt level, CANNOT allocate or move memory.
  364. OSStatus DriverControlCmd (    AddressSpaceID    addressSpaceID,
  365.                             IOCommandID        ioCommandID,
  366.                             IOCommandKind    ioCommandKind,
  367.                             ParmBlkPtr        pb)
  368. {
  369. #pragma unused ( addressSpaceID )
  370.     OSStatus        err            = noErr;
  371.     CntrlParamPtr    pbPtr;
  372.     DriveRecPtr        drive         = &gTheUTGlobals.theDrive;
  373.  
  374.     pbPtr = (CntrlParamPtr) pb;
  375.     
  376.  
  377.     // Check if the control call is one that needs to use the parameter block
  378.     // if so, do the initialization on it.  There should be a check to make sure that
  379.     // the PB is not in use before it is initialized.
  380.     // This method will probably work because we shouldn't be getting these types of requests
  381.     // while reads and writes are happening, but this is not guaranteed
  382.     if ( (pbPtr->csCode == kVerify) || (pbPtr->csCode == kEject) || (pbPtr->csCode == kFormat)
  383.         || ( pbPtr->csCode == 21315 ))
  384.     {
  385.         // Should be a check here to make sure that the parameter block is not in use
  386.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  387.         
  388.         gTheUTGlobals.drivePB.theIOPB            = nil;
  389.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  390.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  391.         gTheUTGlobals.drivePB.retryCount         = 0;
  392.         gTheUTGlobals.drivePB.doWrite             = false;
  393.     }    
  394.  
  395.     // Parse the control codes…
  396.     switch(pbPtr->csCode) 
  397.     {
  398.         case killCode:
  399.         {
  400.             // This driver does not support the killCode,
  401.             // return back -1 as per TechNote DV 17: Sony Driver
  402.             err = -1;
  403.         }
  404.         break;
  405.         
  406.         case kVerify:                        // Verify the media, this should only be called for floppies
  407.         {
  408.             if(gTheUTGlobals.theDrive.capacity == 0)
  409.             {
  410.                 err = nsDrvErr;            // no Media is inserted, return an error
  411.             }
  412.             else if( gTheUTGlobals.isFloppy == true )
  413.             {
  414.                 UInt16     diskCapacity;
  415.                 UInt16     startBlock;
  416.                 UInt16    numberBlocks = 16;
  417.                 UInt8    *blockBuffer;
  418.                 
  419.                 diskCapacity = gTheUTGlobals.theDrive.capacity;
  420.                 blockBuffer = (UInt8 *) NewPtrSysClear(numberBlocks * gTheUTGlobals.theDrive.blockSize);
  421.                 if(blockBuffer == nil)
  422.                 {
  423.                     err = verErr;
  424.                 }
  425.                 else
  426.                 {
  427.                     for (startBlock = 0; startBlock<diskCapacity; startBlock+=numberBlocks)
  428.                     {
  429.                         gTheUTGlobals.doInternalReadWrite = true;
  430.                         err = ReadWriteBlock( &gTheUTGlobals, startBlock, numberBlocks,(Ptr)  blockBuffer, false, false);
  431.                         gTheUTGlobals.doInternalReadWrite = false;
  432.     
  433.                         // an Error has occured                    
  434.                         if ( err != noErr )
  435.                         {
  436.                             // report a verify error
  437.                             err = verErr;
  438.                             break;
  439.                         }
  440.                     }
  441.                     DisposePtr((Ptr) blockBuffer);
  442.                 }
  443.             }
  444.             else
  445.             {
  446.                 err = noErr;            // For the regular cartridge, just report noErr
  447.             }
  448.         }
  449.         break;
  450.  
  451.         case kFormat:
  452.         {
  453.             if (gTheUTGlobals.isWriteProtected == true)
  454.             {
  455.                 // check for write protect
  456.                 err = wPrErr;
  457.                 break;
  458.             }
  459.             
  460.             if(gTheUTGlobals.theDrive.capacity == 0)
  461.             {
  462.                 err = nsDrvErr;            // no Media is inserted, return an error
  463.             }
  464.             else if( gTheUTGlobals.isFloppy == true )
  465.             {
  466.                 gTheUTGlobals.currentExecutionState = kFormatStartState;
  467.                 FormatTheCartridge( &gTheUTGlobals );
  468.                 err = gTheUTGlobals.drivePB.status;
  469.             }
  470.             else
  471.             {
  472.                 err = noErr;            // For the regular cartridge, just report noErr
  473.             }
  474.         }
  475.         break;
  476.  
  477.         case kEject:
  478.         {
  479.             // We get this call whenever a volume is put away (dragged to trash).  
  480.             Boolean hasMountedVolume = (MountedVolOfDrive(drive) != nil);
  481.  
  482.             if (!hasMountedVolume)                    // If no more mounted volumes…
  483.             {
  484.                 gTheUTGlobals.currentExecutionState = kEjectStartState;
  485.                  EjectTheCartridge( &gTheUTGlobals );
  486.             }
  487.             
  488.         }
  489.         break;
  490.  
  491.         case kSetTagBuffer:                    // This is a floppy specific control call
  492.         {            
  493.             err = controlErr;                // Return a controlErr, since we do not support this call
  494.         }
  495.         break;
  496.  
  497.         case kTrackCache:                    // This is a floppy specific control call
  498.         {            
  499.             err = controlErr;                // The driver does not keep an internal write cache,
  500.                                             // therefore report that we do not support this call
  501.         }
  502.         break;
  503.  
  504.         case kDriveIcon:                    // Return icon displayed during media initialization
  505.         case kMediaIcon:                    // Return icon displayed on desktop for media
  506.         {
  507.             VolumeRecPtr    vol         = nil;        // pointer to our volume record structure
  508.             
  509.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  510.             if ( !vol )                                // If we did not find a drive and volume
  511.             {
  512.                 err = nsDrvErr;                        // report drive not found error
  513.                 break;
  514.             }
  515.                 
  516.             BlockMove(vol->mediaIconPtr, &gDriveIcon.LocationIcon, sizeof(DiskIcon));
  517.  
  518.             // Copy over the DriveInfo string from the global param block        
  519.             PStrCopy(gDriveIcon.LocationString, gTheUTGlobals.DriveInfoString);
  520.  
  521.             // Finally, return the pointer to the icon in csParam
  522.             *(DriverLocationIcon**)pbPtr->csParam = &gDriveIcon;    
  523.  
  524.             err = noErr;                // clear any error from above
  525.         }
  526.         break;
  527.  
  528.         case kDriveInfo:                    // DRIVE INFO request (was from ATA maanger)
  529.         {
  530.             pbPtr->csParam[0] = 0;            // Upper Word is always 0;
  531.             pbPtr->csParam[1] = 0;            // Clear Lower Word
  532.             
  533.             if( gTheUTGlobals.isFloppy == true )
  534.             {
  535.                 // If we currently have a floppy loaded, return the following info
  536.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  537.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  538.                                     |     ( 0 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  539.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  540.                                                         // Bits 4,5,6,7 are reserved
  541.                                     |     4;                // Drive Type ( use 4 for SuperDrive for compatibility )
  542.             }
  543.             else if(gTheUTGlobals.theDrive.blockSize != 0)
  544.             {
  545.                 // If we currently have a cartridge loaded, return the following info
  546.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  547.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  548.                                     |     ( 1 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  549.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  550.                                                         // Bits 4,5,6,7 are reserved
  551.                                     |     1;                // Drive Type ( use 1 for Unknown drive (Not a floppy) )
  552.             }
  553.  
  554.         }
  555.         break;
  556.  
  557.         
  558.         case kDriverConfigureCode:
  559.         {
  560.             DriverConfigParam             *configPtr;            // local pointer to drive config structure
  561.                         
  562.             configPtr = (DriverConfigParam *) pbPtr;
  563.             switch(configPtr->driverConfigureSelector) 
  564.             {
  565.                 case kdgFlush:
  566.                 {
  567.                     // Since cache flushing differs between devices, this is not implemented in the sample driver.
  568.                     err = controlErr;
  569.                 }
  570.                 break;
  571.                 
  572.                 default:
  573.                 {
  574.                     err = controlErr;
  575.                 }
  576.                 break;
  577.             }
  578.         }
  579.         break;
  580.  
  581.         case kcsSetBootPartitionCode:                // Set Startup Partition
  582.         {
  583.             // Since there is currently no way to boot of USB,
  584.             // this control call will report that it is not supported
  585.             err = controlErr;
  586.         }
  587.         break;
  588.  
  589.         case kRegisterPartition:            // Register New Partition
  590.         {
  591.             // PC Exchange will call this function when it wants to redefine a partition.  It will
  592.             // pass in the drive queue element pointer of the partition to redefine, the
  593.             // new starting physical block offset, and the new block length.
  594.  
  595.             DrvQElPtr        theDrvQEl;                                // drive queue element pointer
  596.             VolumeRecPtr    vol         = nil;                        // pointer to our volume record structure
  597.             UInt32            *altParams;                                // local pointer to alternate control parameters
  598.     
  599.             altParams = (UInt32 *)&pbPtr->csParam[0];                // alternate parameters
  600.             
  601.             err = nsDrvErr;                                            // assume an invalid volume
  602.             theDrvQEl = (DrvQElPtr) altParams[THE_DRIVE];
  603.             if ( theDrvQEl )                                        // if valid queue element pointer
  604.             {
  605.                 vol = GetVolume(drive, theDrvQEl->dQDrive, 0);
  606.                 if ( vol )                                            // and valid volume reference
  607.                 {
  608.                     vol->partoffset = altParams[THE_PHYS_START];    // new partition offset
  609.                     vol->curoffset = vol->partoffset;                // current offset changes also
  610.                     vol->partblks = altParams[THE_PHYS_SIZE];        // new partition size
  611.                     
  612.                     UpdateQ(theDrvQEl->dQDrive, vol->partblks);        // Update drive queue capacity
  613.                     vol->partmounted = true;                        // volume will be mounted by PCX
  614.                     err = noErr;                                    // clear error
  615.                 }
  616.             }
  617.         }
  618.         break;
  619.             
  620.         case kGetADrive:                    // Get A Drive (Create New Partition)
  621.         {            
  622.             // PC Exchange calls this function to add a new partition.  The new partition's DrvQElPtr is
  623.             // returned. NOTE: If the driver handles multiple drives note that PC Exchange does not pass
  624.             // in the physical drive number on which to create the new partition.  However, the
  625.             // DrvQElPtr stored at the pointer passed in is for another partition on the drive.
  626.             UInt32            *altParams;                        // local pointer to alternate control parameters
  627.     
  628.             altParams = (UInt32 *)&pbPtr->csParam[0];        // alternate parameters
  629.         
  630.             if (!altParams[THE_VAR_QUEL])        // verify a valid queue element handle
  631.                 err = paramErr;
  632.             else
  633.             {
  634.                 // create a new volume record and DrvQEl associated with the physical drive.
  635.                 // The new volume starts at offset 0 and has no capacity yet. By default, the
  636.                 // partition will not have a partition map entry on the media.
  637.                 VolumeRecPtr    vol         = nil;            // pointer to our volume record structure
  638.                     
  639.                 vol = CreateVolume(drive, NextPartitionID(drive), 0, 0 );
  640.                 if ( vol ) 
  641.                 {
  642.                     UInt16 volNumber;
  643.                     
  644.                     vol->vRefNum = NextQDrive();        // assign a logical drive number
  645.                     volNumber = vol->vRefNum;
  646.                     
  647.                     NativeAddDrive(gTheUTGlobals.drvrRefNum, volNumber,(DrvQElPtr)  &vol->driveStatus.qLink);
  648.  
  649.                     // Return the DrvQElPtr at the location passed in…
  650.                     *((DrvQElPtr*)altParams[THE_VAR_QUEL]) = (DrvQElPtr) &vol->driveStatus.qLink;
  651.                 }
  652.                 else
  653.                     err = controlErr;
  654.             }
  655.         }
  656.         break;
  657.             
  658.         case kMediaPowerCSCode:                        // Set Power Mode
  659.         {
  660.             // The driver does not support power control modes
  661.             // report back a controlErr.
  662.             err = controlErr;
  663.         }
  664.         break;
  665.  
  666.         case 500:
  667.         {
  668.             AbsoluteTime    theWait;
  669.         
  670.             IfDebugging("\pSet Dispatch Table");
  671.             gItsTheDispatchTable = ( StorageClassDispatchTablePtr ) *((UInt32 *)(&pbPtr->csParam[0]));                // The class's dispatch table
  672.  
  673.             (void) ((gItsTheDispatchTable)->pStorageControl)(kControlDisableRemoval, nil);
  674.             theWait = DurationToAbsolute(durationSecond);
  675.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  676.             err = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  677.         }
  678.         break;
  679.  
  680.         case 21315:        // Disk copy format and copy
  681.         {
  682.             UInt16        theFormat;
  683.             UInt8        *theDiskImage;
  684.             
  685.             //SysDebugStr("\pDiskCopy");
  686.  
  687.             if( gTheUTGlobals.isWriteProtected == true)
  688.             {
  689.                 err = wPrErr;            // disk is write protected
  690.                 break;
  691.             }
  692.             
  693.             theFormat = pbPtr->csParam[0];
  694.             theDiskImage = (UInt8 *) *((UInt32 *) &pbPtr->csParam[1]);        // Extract the pointer to the data
  695.             gTheUTGlobals.doInternalReadWrite = true;
  696.             err = ReadWriteBlock( &gTheUTGlobals, 0, gTheUTGlobals.theDrive.capacity,(Ptr)  theDiskImage, true, false );
  697.             gTheUTGlobals.doInternalReadWrite = false;
  698.  
  699.             // an Error has occured                    
  700.             if ( err != noErr )
  701.             {
  702.                 // report an error
  703.                 err = paramErr;
  704.             }
  705.         }
  706.         break;
  707.         
  708.         default:
  709.         {
  710.             err = controlErr;
  711.         }
  712.         break;
  713.     }
  714.         
  715.     //SysDebugStr("\pEnd Control Call");
  716.     return(err);
  717. }
  718.  
  719. //    May run at interrupt level, CANNOT allocate or move memory.
  720.  
  721. OSStatus DriverStatusCmd (    AddressSpaceID    addressSpaceID,
  722.                             IOCommandID        ioCommandID,
  723.                             IOCommandKind    ioCommandKind,
  724.                             ParmBlkPtr        pb)
  725. {
  726. #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
  727.  
  728.     OSStatus            err            = noErr;
  729.     CntrlParamPtr         pbPtr;
  730.     DriveRecPtr            drive         = &gTheUTGlobals.theDrive;
  731.  
  732. #if 0
  733.     // This can be removed because this routine never invokes device access
  734.     // and always complete immediately.  If this a status call is added that
  735.     // doesn't complete immediately, then this routine will have to guarantee
  736.     // that the parameter block that it uses is not already in use.
  737.     BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  738.     
  739.     gTheUTGlobals.drivePB.theIOPB            = nil;
  740.     gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  741.     gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  742.     gTheUTGlobals.drivePB.retryCount         = 0;
  743.     gTheUTGlobals.drivePB.doWrite             = false;
  744. #endif
  745.  
  746.     pbPtr = (CntrlParamPtr) pb;
  747.  
  748.     // Finish processing the status call…
  749.     switch(pbPtr->csCode) 
  750.     {
  751.         case kReturnFormatList:
  752.         {
  753.             if( gTheUTGlobals.isFloppy == false )
  754.             {
  755.                 err = statusErr;
  756.             }
  757.             else
  758.             {
  759.                 typedef struct FormatList
  760.                 {
  761.                     UInt32        NumberBlock;
  762.                     UInt8        TSSValid         : 1;
  763.                     UInt8        IsCurrentFormat : 1;
  764.                     UInt8        CanFormat        : 1;
  765.                     UInt8        Density            : 1;
  766.                     UInt8        NoSides            : 4;
  767.                     UInt8        SecPerTrack;
  768.                     UInt16        NumberTracks;
  769.                 } FormatList;
  770.                 
  771.                 UInt8            returnNumber;
  772.                 UInt8            totalFormats = 2;
  773.                 FormatList        theFormats[2];
  774.                 
  775.                 // Setup the Formats
  776.                 // Double Density Floppy Disk
  777.                 theFormats[0].NumberBlock        = 1440;
  778.                 theFormats[0].TSSValid            = 1;
  779.                 theFormats[0].Density            = 0;            // 0 means single density, 1 means Double Density
  780.                 theFormats[0].NoSides            = 2;
  781.                 theFormats[0].SecPerTrack        = 9;
  782.                 theFormats[0].NumberTracks        = 80;
  783.                 
  784.                 // High Density Floppy Disk
  785.                 theFormats[1].NumberBlock        = 2880;
  786.                 theFormats[1].TSSValid            = 1;
  787.                 theFormats[1].Density            = 1;
  788.                 theFormats[1].NoSides            = 2;
  789.                 theFormats[1].SecPerTrack        = 18;
  790.                 theFormats[1].NumberTracks        = 80;
  791.  
  792.                 if( drive->capacity < 0x600 )
  793.                 {
  794.                     theFormats[0].CanFormat            = 0;        // 0 means we can format this disk as 720K
  795.                     theFormats[1].CanFormat            = 1;        // 1 means we can not format this disk as 1.44M
  796.                     theFormats[0].IsCurrentFormat    = 1;         // 1 means 720K MFM Disk is installed
  797.                     theFormats[1].IsCurrentFormat     = 0;        // 0 means 1.44M MFM Disk is not installed
  798.                 }
  799.                 else
  800.                 {
  801.                     theFormats[0].CanFormat            = 1;        // 1 means we can not format this disk as 720K
  802.                     theFormats[1].CanFormat            = 0;        // 0 means we can format this disk as 1.44M
  803.                     theFormats[0].IsCurrentFormat    = 0;         // 0 means 720K MFM Disk is not installed
  804.                     theFormats[1].IsCurrentFormat     = 1;        // 1 means 1.44M MFM Disk is installed
  805.                 }
  806.  
  807.                 returnNumber = *((UInt16 *) &pbPtr->csParam[0]);
  808.                 if( totalFormats < returnNumber )
  809.                 {
  810.                     returnNumber = totalFormats;
  811.                 }
  812.                 
  813.                 BlockCopy( (Ptr) &theFormats[0], (Ptr) (*((UInt32 *) &pbPtr->csParam[1])), sizeof(FormatList) * returnNumber);
  814.                 *((UInt16 *) &pbPtr->csParam[0]) = returnNumber;
  815.             }
  816.         }
  817.         break;
  818.     
  819.         case kDriveStatus:
  820.         {
  821.             VolumeRecPtr        vol         = nil;
  822.             
  823.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  824.             BlockMove(&vol->driveStatus, (UInt8 *) &pbPtr->csParam[0], sizeof(DrvSts));
  825.         }
  826.         break;
  827.     
  828.         case kMFMStatus:
  829.         {
  830.             if( gTheUTGlobals.isFloppy == false )
  831.             {
  832.                 err = statusErr;
  833.             }
  834.             else
  835.             {
  836.                 pbPtr->csParam[0] = -3;                            // PC Industry standard MFM (no GCR support)
  837.                 pbPtr->csParam[1] = -1;                            // MFM Disk installed
  838.                 
  839.                 if( drive->capacity < 0x600 )
  840.                 {
  841.                     pbPtr->csParam[2] = 0;                        // 720K MFM Disk installed
  842.                 }
  843.                 else
  844.                 {
  845.                     pbPtr->csParam[2] = -1;                        // 1.44M MFM Disk installed
  846.                 }
  847.                 
  848.                 pbPtr->csParam[3] = -5;                            // Generic PC Floppy Disk Controller
  849.             }
  850.         }
  851.         break;
  852.     
  853.         case kDriverGestaltCode:
  854.         {
  855.             DriverGestaltParam             *gestaltPtr;            // local pointer to drive gestalt structure
  856.             VolumeRecPtr                vol = nil;
  857.  
  858.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  859.             if (vol)
  860.                 drive = (DriveRecPtr) vol->drivePtr;            // get its drive record
  861.  
  862.             gestaltPtr = (DriverGestaltParam *) pbPtr;
  863.             switch(gestaltPtr->driverGestaltSelector) 
  864.             {
  865.                 case kdgVersion:
  866.                 {
  867.                     // Return information on the driver version
  868.                     NumVersion* numVersion;
  869.                     
  870.                     numVersion = GetDriverGestaltVersionResponse(gestaltPtr);
  871.                     
  872.                     numVersion->majorRev =            kStorageHexMajorVers;
  873.                     numVersion->minorAndBugRev =    kStorageHexMinorVers;
  874.                     numVersion->stage =                kStorageReleaseStage;
  875.                     numVersion->nonRelRev =            kStorageCurrentRelease;
  876.                 }
  877.                 break;
  878.                 
  879.                 case kdgDeviceType:
  880.                 {
  881.                     // Return the type of device--either floppy disk or removable disk
  882.                     DriverGestaltDevTResponse* deviceTypeResponse = GetDriverGestaltDevTResponse(gestaltPtr);
  883.  
  884.                     if( gTheUTGlobals.isFloppy == true )
  885.                     {
  886.                         deviceTypeResponse->deviceType = kdgFloppyType;
  887.                     }
  888.                     else
  889.                     {
  890.                         deviceTypeResponse->deviceType = kdgRemovableType;
  891.                     }
  892.                 }
  893.                 break;
  894.                 
  895.                 case kdgInterface:
  896.                 {
  897.                     // Return the interface of the drive in ioVRefNum.
  898.                     DriverGestaltIntfResponse* interfaceResponse = GetDriverGestaltIntfResponse(gestaltPtr);        
  899.  
  900.                     interfaceResponse->interfaceType = 'USB ';
  901.                 }
  902.                 break;
  903.  
  904.                 case kdgSync:
  905.                 {
  906.                     // Return true if the driver supports only synchronous behavior
  907.                     DriverGestaltSyncResponse*    syncResponse = GetDriverGestaltSyncResponse(gestaltPtr);
  908.  
  909.                     syncResponse->behavesSynchronously = false;
  910.                 }
  911.                 break;
  912.                     
  913.                 case kdgBoot:
  914.                 {
  915.                     // Return the ID of the boot device for PRAM storage
  916.                     if (!drive || !vol)
  917.                         err = nsDrvErr;
  918.                     else
  919.                     {
  920.                         // Since booting from USB is not yet supported, return statusErr.
  921.                         err = statusErr;
  922.                     }
  923.                 }
  924.                 break;
  925.  
  926.                 case kdgWide:
  927.                 {
  928.                     // Return whether driver supports large volume addressing (> 4GByte)
  929.                     // Unless our cartridges are greater than 4 GB, we don't support
  930.                     // or need to support wide block addressing
  931.                     Boolean* response = GetDriverGestaltBooleanResponse(gestaltPtr);
  932.                     
  933.                     *response = false; 
  934.                 }
  935.                 break;
  936.  
  937.                 case kdgPurge:        // Return if we can be closed and purged from memory.
  938.                 {
  939.                     DriverGestaltPurgeResponse* response = GetDriverGestaltPurgeResponse(gestaltPtr);
  940.                                         
  941.                     // Currently the driver cannot be closed or purged once installed
  942.                     response->purgePermission = kmNoCloseNoPurge;
  943.  
  944.                     response->purgeDriverPointer = (Ptr) nil;
  945.                 }
  946.                 break;
  947.                     
  948.                 case kdgSupportsSwitching:
  949.                 {
  950.                     // Return whether driver supports low power control call (csCode = 70h)
  951.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  952.                 }
  953.                 break;
  954.                     
  955.                 case kdgSupportsPowerCtl:
  956.                 {
  957.                     // Return whether driver supports low power control call (csCode = 70h)
  958.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  959.                 }
  960.                 break;
  961.                     
  962.                 case kdgAPI:
  963.                 {
  964.                     // Return whether driver supports PC-Exchange Control and Status calls
  965.                     // related to partitioning.
  966.                     DriverGestaltAPIResponse* apiResponse = GetDriverGestaltAPIResponse(gestaltPtr);
  967.                     
  968.                     apiResponse->partitionCmds = true;
  969.                 }
  970.                 break;
  971.                 
  972.                 case kdgFlush:
  973.                 {
  974.                     // Return whether driver supports the Cache flush Control call,
  975.                     // and whether the finder should tell us to flush our cache
  976.                     // Since cache flushing differs between devices, this is not implemented in the sample driver.
  977.                     DriverGestaltFlushResponse* response = GetDriverGestaltFlushResponse(gestaltPtr);
  978.  
  979.                     response->canFlush = false;
  980.                     response->needsFlush = false;
  981.                 }
  982.                 break;
  983.  
  984.                 case kdgEject:
  985.                 {
  986.                     // Return whether driver wants eject call for shutdown and restart
  987.                     // Eject on restart or shutdown
  988.                     DriverGestaltEjectResponse* response = GetDriverGestaltEjectResponse(gestaltPtr);
  989.                     response->ejectFeatures = 0L; // Clear both Dont Eject Bits, kShutDownDontEject and kRestartDontEject.
  990.                 }
  991.                 break;
  992.  
  993.                 case kdgVMOptions:
  994.                 {
  995.                     // Return whether drive can be used for Virtual Memory
  996.                     // Don't support use of media for VM
  997.                     DriverGestaltVMOptionsResponse* response = GetDriverGestaltVMOptionsResponse(gestaltPtr);
  998.                     response->vmOptions = kAllowVMNoneMask;
  999.                 }
  1000.                 break;
  1001.  
  1002.                 case kdgMediaInfo:
  1003.                 {
  1004.                     // Return back specific information about our media
  1005.                     DriverGestaltMediaInfoResponse* response = GetDriverGestaltMediaInfoResponse(gestaltPtr);
  1006.                     //response->numberBlocks =    vol->partblks;
  1007.                     response->numberBlocks =    gTheUTGlobals.theDrive.capacity;
  1008.                     response->blockSize =        gTheUTGlobals.theDrive.blockSize;
  1009.                     if(gTheUTGlobals.theDrive.blockSize == 0)
  1010.                     {
  1011.                         response->mediaType =        kMediaTypeNoMedia;
  1012.                     }
  1013.                     else
  1014.                     {
  1015.                         response->mediaType =        kMediaTypeUnknown;
  1016.                     }
  1017.                 }
  1018.                 break;
  1019.  
  1020.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  1021.                 /* Disk Driver physical drive (formerly in csCode 22) in driverGestaltResponse. */
  1022.                 case kdgPhysDriveIconSuite:
  1023.  
  1024.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  1025.                 /* Disk Driver media (formerly in csCode 21) in driverGestaltResponse. */
  1026.                 case kdgMediaIconSuite:
  1027.                 {
  1028.                     // If the media is currently a floppy, return a statusErr so the system will
  1029.                     // handle the 3D Color icon information
  1030.                     if( gTheUTGlobals.isFloppy == true )
  1031.                     {
  1032.                         if(FloppyMediaIconFamily == nil )
  1033.                         {
  1034.                             err = statusErr;
  1035.                         }
  1036.                         else
  1037.                         {
  1038.                             gestaltPtr->driverGestaltResponse = (UInt32) *FloppyMediaIconFamily;
  1039.                         }
  1040.                     }
  1041.                     else
  1042.                     {
  1043.                         if(CartridgeMediaIconFamily == nil )
  1044.                         {
  1045.                             err = statusErr;
  1046.                         }
  1047.                         else
  1048.                         {
  1049.                             gestaltPtr->driverGestaltResponse = (UInt32) CartridgeMediaIconFamily;
  1050.                         }
  1051.                     }
  1052.                 }
  1053.                 break;
  1054.  
  1055.                 case kdgMediaName:
  1056.                 {
  1057.                     /* Return a pointer to a pascal string describing the Disk Driver (formerly in csCode 21) in driverGestaltResponse. */
  1058.                     gestaltPtr->driverGestaltResponse = (UInt32) &gTheUTGlobals.DriveInfoString;
  1059.                 }
  1060.                 break;
  1061.  
  1062.                 default: 
  1063.                     err = statusErr;        // unknown DriverGestalt selector
  1064.             }
  1065.         }
  1066.         break;
  1067.         
  1068.         case kcsGetBootPartitionStatus:     // Is this the boot partition?
  1069.         {
  1070.             // Since USB drives don't support booting, always report false
  1071.             err = statusErr;
  1072.         }
  1073.         break;
  1074.         
  1075.         case kGetPartInfo:
  1076.         {
  1077.             // PC Exchange will call this function to get info on the specified partition. 
  1078.             // Return the physical drive reference, the starting block offset, and 
  1079.             // the partition ID (any relative non-zero reference).
  1080.     
  1081.             UInt32            *altParams;                // alternate csParams as long words
  1082.             partInfoRecPtr     thePartInfo;
  1083.             VolumeRecPtr    vol = nil;
  1084.             
  1085.             altParams = (UInt32 *) &pbPtr->csParam[0];            // alternate parameters
  1086.             thePartInfo = (partInfoRecPtr)altParams[kPartInfoResponse];
  1087.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  1088.     
  1089.             // SCSIID is not defined for non-SCSI devices.  For now we use the LUN only
  1090.             *((UInt32 *)&thePartInfo->SCSIID) = 0;
  1091.     
  1092.             thePartInfo->physPartitionLoc = vol->partoffset;
  1093.             thePartInfo->partitionNumber = vol->partitionNo;
  1094.         }
  1095.         break;
  1096.     
  1097.         case kMediaPowerCSCode:
  1098.         {
  1099.             // Driver does not support power control modes,
  1100.             // so just return a statusErr
  1101.             err = statusErr;
  1102.         }
  1103.         break;        
  1104.         
  1105.         case 17494:                            // DiskCopy version supported
  1106.         {
  1107.             pbPtr->csParam[0] = 0x0410;        // We support the Diskcopy 4.1 API
  1108.         }
  1109.         break;
  1110.         
  1111.         default:                            // Unrecognized status call
  1112.         {
  1113.             err = statusErr;
  1114.         }
  1115.         break;
  1116.     }
  1117.     //SysDebugStr("\pEnd Status Call");
  1118.     
  1119.     return(err);
  1120. }
  1121.  
  1122. //    May run at interrupt level, CANNOT allocate or move memory.
  1123.  
  1124. OSStatus DriverReadCmd (    AddressSpaceID    addressSpaceID,
  1125.                             IOCommandID        ioCommandID,
  1126.                             IOCommandKind    ioCommandKind,
  1127.                             ParmBlkPtr        pb)
  1128. {
  1129. #pragma unused ( addressSpaceID )
  1130.     OSStatus        err = ioErr;
  1131.  
  1132.     if( gTheUTGlobals.diskInDrive == false)
  1133.     {
  1134.         err = ioErr;
  1135.     }
  1136.     else
  1137.     {
  1138.         //SysDebugStr("\pRead Call;g");
  1139.         // Clear out the Drive request PB
  1140.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  1141.         
  1142.         gTheUTGlobals.drivePB.theIOPB            = pb;
  1143.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  1144.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  1145.         gTheUTGlobals.drivePB.retryCount         = 0;
  1146.         gTheUTGlobals.drivePB.doWrite             = false;
  1147.     
  1148.         err = DoReadWriteCommand( &gTheUTGlobals, false);
  1149.     }
  1150.     return(err);
  1151. }
  1152.  
  1153. //    May run at interrupt level, CANNOT allocate or move memory.
  1154.  
  1155. OSStatus DriverWriteCmd (    AddressSpaceID    addressSpaceID,
  1156.                             IOCommandID        ioCommandID,
  1157.                             IOCommandKind    ioCommandKind,
  1158.                             ParmBlkPtr        pb)
  1159. {
  1160. #pragma unused ( addressSpaceID )
  1161.     OSStatus        err = ioErr;
  1162.  
  1163.     if( gTheUTGlobals.diskInDrive == false )
  1164.     {
  1165.         err = ioErr;
  1166.     }
  1167.     else
  1168.     {
  1169.         //SysDebugStr("\pWrite Call");
  1170.         // Clear out the Drive request PB
  1171.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  1172.     
  1173.         gTheUTGlobals.drivePB.theIOPB            = pb;
  1174.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  1175.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  1176.         gTheUTGlobals.drivePB.retryCount         = 0;
  1177.         gTheUTGlobals.drivePB.doWrite             = true;
  1178.             
  1179.         err = DoReadWriteCommand( &gTheUTGlobals, true);
  1180.     }
  1181.     
  1182.     return(err);
  1183. }
  1184.  
  1185. //    May run at interrupt level, CANNOT allocate or move memory.
  1186.  
  1187. OSStatus DriverKillIOCmd (ParmBlkPtr pb)
  1188. {
  1189. #pragma unused (pb)
  1190.     OSStatus    err = noErr;
  1191.  
  1192.     return (err);
  1193. }
  1194.  
  1195. void InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec)    
  1196. {
  1197.     Boolean hasPMDispatch;
  1198.     SInt32    status;
  1199.  
  1200.     // To do most power management we must have the Power Manager Dispatch routines.      
  1201.     if (Gestalt(gestaltPowerMgrAttr, &status) == 0)                                
  1202.         hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false;    
  1203.  
  1204.     if (hasPMDispatch)                                                            
  1205.     {
  1206.         // Power Manager manages a spindown timer for the internal drive.  When the
  1207.         // timer expires it calls all routines registered in the HD Queue, and then,
  1208.         // if PG&E is present (Powerbooks) it will turn off power to the drive.
  1209.         // If PG&E is not present (Desktops) Power Manager can't turn off power and
  1210.         // expects one of the queue routines to reduce drive power instead. 
  1211.         //  Therefore, if we manage the internal drive we should be in the HD Queue.
  1212.  
  1213.             ourSleepQRec->theSleepQRec.sleepQLink = nil;                                
  1214.             ourSleepQRec->theSleepQRec.sleepQType = sleepQType;                            
  1215.             ourSleepQRec->theSleepQRec.sleepQProc = NewSleepQProc( &SleepNotification);        
  1216.             //ourSleepQRec->theSleepQRec.sleepQProc = (SleepQUPP) NewSleepQProc( &SleepNotification);        
  1217.             ourSleepQRec->theSleepQRec.sleepQFlags = 0;                // reserved    
  1218.             ourSleepQRec->isInSleep = false;    
  1219.             SleepQInstall( (SleepQRecPtr) ourSleepQRec );                            
  1220.     }                                                                            
  1221. }                                                                                
  1222.  
  1223. void RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec)    
  1224. {
  1225.     Boolean hasPMDispatch;
  1226.     SInt32    status;
  1227.  
  1228.     // To do most power management we must have the Power Manager Dispatch routines.      
  1229.     if (Gestalt(gestaltPowerMgrAttr, &status) == 0)                                
  1230.         hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false;    
  1231.  
  1232.     if (hasPMDispatch)                                                            
  1233.     {
  1234.         SleepQRemove( (SleepQRecPtr) ourSleepQRec );
  1235.     }
  1236. }                                                                                
  1237.  
  1238.  
  1239. long SleepNotification(long message, SleepQRecPtr sleepRec)
  1240. {
  1241.     OurSleepQRecPtr    ourSleepRec = (OurSleepQRecPtr) sleepRec;
  1242.     SInt32             response = noErr;                // assume we accept command
  1243.  
  1244.     switch(message)
  1245.     {
  1246.         case dozeRequest:                    // ##### Request to doze ######
  1247.         case dozeDemand:                    // ##### Going to doze now ######
  1248.         case sleepRequest:                    // ##### Request to sleep ######
  1249.         case sleepDemand:                    // ##### Going to sleep now ######
  1250.         case sleepNow:
  1251.             ourSleepRec->isInSleep = true;
  1252.             break;
  1253.  
  1254.         case sleepWakeUp:                    // ##### Wakeup from sleep ######
  1255.         case sleepRevoke:                    // ##### Someone denied sleep ######
  1256.         case dozeWakeUp:                    // ##### Wakeup from doze ######
  1257.             ourSleepRec->isInSleep = false;
  1258.             break;
  1259.     }
  1260.  
  1261.     return(response);
  1262. }
  1263.  
  1264.  
  1265. OSStatus MountSecondaryInterrupt( void *p1, void *p2)
  1266. {
  1267. #pragma unused ( p1, p2 )
  1268.     OSStatus             status;
  1269.     AbsoluteTime        theWait;
  1270.     UInt32                classDriverStatus;
  1271.  
  1272.     IfDebugging("\pMountSecondaryInterrupt");    
  1273.     gInterruptTimer = 0;
  1274.     
  1275.     if(gItsTheDispatchTable)
  1276.     {
  1277.         // Check if class driver is configured, if not setup another interrupt
  1278.         status = ((gItsTheDispatchTable)->pStorageStatus)(kStatusConfiguration, &classDriverStatus);
  1279.         if (status == noErr)
  1280.         {
  1281.             if ((classDriverStatus == kConfigureInProgress) || (gTheUTGlobals.theSleepQRec.isInSleep == true))
  1282.             {
  1283.                 theWait = DurationToAbsolute(durationSecond);
  1284.                 theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1285.                 status = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1286.                 
  1287.                 return noErr;
  1288.             } 
  1289.             else if (classDriverStatus == kConfigureComplete)
  1290.             {
  1291.                 gTheUTGlobals.currentExecutionState = kMountStartState;
  1292.                 MountTheCartridge( &gTheUTGlobals );
  1293.             }
  1294.         }
  1295.     }
  1296.     
  1297.     return noErr;
  1298. }
  1299.  
  1300.  
  1301. void MountTheCartridge( void *theCurrentPB )
  1302. {
  1303.     OSStatus         err = noErr;
  1304.     AbsoluteTime    oneSecondWait;
  1305.     DriveRec        *drive;
  1306.     UTDriverGlobals    *ourPB;
  1307.  
  1308.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1309.     drive = &ourPB->theDrive;
  1310.     oneSecondWait = DurationToAbsolute(durationSecond);
  1311.     //oneSecondWait = DurationToAbsolute(durationMillisecond * 20);
  1312.     switch (ourPB->currentExecutionState )
  1313.     {
  1314.         case kMountStartState:
  1315.         {
  1316.             ourPB->currentExecutionState = kMountTURDoneState;
  1317.             err = TUR( ourPB, &MountTheCartridge);
  1318.         }
  1319.         break;
  1320.         
  1321.         case kMountTURDoneState:
  1322.         {
  1323.             ourPB->currentExecutionState = kMountRequestSenseDoneState;
  1324.             err = RequestSense( ourPB, &MountTheCartridge);
  1325.         }
  1326.         break;
  1327.         
  1328.         case kMountRequestSenseDoneState:
  1329.         {
  1330.             if((ourPB->sense[12] == 0x00) && (ourPB->drivePB.executePB.status == noErr))
  1331.             {
  1332.                 ourPB->currentExecutionState = kMountGetGeometryDoneState;
  1333.                 ourPB->getCapacity.lastLogicalBlock = 0;
  1334.                 ourPB->getCapacity.blockLength = 0;
  1335.                 err = GetMediaGeometry( ourPB, &MountTheCartridge);
  1336.             }
  1337.             else
  1338.             {
  1339.                 ourPB->currentExecutionState = kMountStartState;
  1340.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1341.                 err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1342.                 err = noErr;
  1343.             }
  1344.         }
  1345.         break;
  1346.  
  1347.         case kMountGetGeometryDoneState:
  1348.         {
  1349.             ReadCapacityData        *getGeometry;
  1350.             
  1351.             getGeometry = (ReadCapacityData *) &ourPB->getCapacity;
  1352.  
  1353.             drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in
  1354.             drive->blockSize = getGeometry->blockLength;
  1355.             
  1356.             if((drive->capacity == 0) || (drive->blockSize == 0))
  1357.             {
  1358.                 //Find out why the capacity is zero
  1359.                 ourPB->currentExecutionState = kMountCheckGetGeometryErrorDone;
  1360.                 err = RequestSense( ourPB, &MountTheCartridge);
  1361.                 break;
  1362.             }
  1363.  
  1364.             if(    drive->capacity >0x0C00)
  1365.             {
  1366.                 ourPB->isFloppy = false;
  1367.             }
  1368.             else
  1369.             {
  1370.                 ourPB->isFloppy = true;
  1371.             }
  1372.             
  1373.             ourPB->diskInDrive = true;
  1374.             
  1375.             // Check to see if media is write protected
  1376.             ourPB->currentExecutionState = kMountCheckWriteProtectDoneState;
  1377.             err = CheckWriteProtect(ourPB, &MountTheCartridge);
  1378.         }
  1379.         break;
  1380.  
  1381.         case kMountCheckGetGeometryErrorDone:
  1382.         {
  1383.             if( (ourPB->sense[12] == 0x30) && (ourPB->sense[13] == 0x01))
  1384.             {
  1385.                 // We have an unformatted cartridge, find out what format it can have
  1386.                 ourPB->currentExecutionState = kMountReadPossibleCapacitiesDone;
  1387.                 err = ReadFormatCapacity(ourPB, &MountTheCartridge);
  1388.             }
  1389.             else
  1390.             {
  1391.                 ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1392.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1393.                 err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1394.             }
  1395.         }
  1396.         break;
  1397.         
  1398.         case kMountReadPossibleCapacitiesDone:
  1399.         {
  1400.             if( (ourPB->sense[3] >= 0x08) && (ourPB->sense[8] & 0x01 == 0x01))
  1401.             {
  1402.                 // this is the maximum format for this cartridge
  1403.                 drive->capacity = *((UInt32 *) &ourPB->sense[4]);
  1404.                 drive->blockSize = *((UInt16 *) &ourPB->sense[10]);
  1405.  
  1406.                 if(    drive->capacity >0x0C00)
  1407.                 {
  1408.                     ourPB->isFloppy = false;
  1409.                 }
  1410.                 else
  1411.                 {
  1412.                     ourPB->isFloppy = true;
  1413.                 }
  1414.                 
  1415.                 // Check to see if media is write protected
  1416.                 ourPB->currentExecutionState = kMountCheckWriteProtectDoneState;
  1417.                 err = CheckWriteProtect(ourPB, &MountTheCartridge);
  1418.                 break;
  1419.             }
  1420.  
  1421.             // An error occurred.  It could be:
  1422.             //        1. we didn't get a complete descriptor
  1423.             //         2. the drive doesn't recognize this media type 
  1424.             // Reset and try again. ( Should we possibly eject the media instead??)
  1425.             ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1426.             oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1427.             err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1428.         }
  1429.         break;
  1430.         
  1431.         case kMountCheckWriteProtectDoneState:
  1432.         {
  1433.             if ( ( ourPB->sense[3] & 0x80 ) != 0 )
  1434.             {
  1435.                 ourPB->isWriteProtected = true;
  1436.             }
  1437.             else
  1438.             {
  1439.                 ourPB->isWriteProtected = false;
  1440.             }
  1441.             
  1442.             ourPB->currentExecutionState = kMountPreventRemovalDoneState;
  1443.             err = PreventAllowRemoval(ourPB, true, &MountTheCartridge);
  1444.         }
  1445.         break;
  1446.         
  1447.         case kMountPreventRemovalDoneState:
  1448.         {
  1449.             // The device is now mounted, and the Media is locked in place.
  1450.             // There is nothing left for us to do, so just break.
  1451.             if (InstallDrive( drive, true ) == false)
  1452.             {
  1453.                 gTheUTGlobals.currentExecutionState = kEjectStartState;
  1454.                  EjectTheCartridge( &gTheUTGlobals );
  1455.             }
  1456.         }
  1457.         break;
  1458.     }
  1459. }
  1460.  
  1461. void EjectTheCartridge( void *theCurrentPB )
  1462. {
  1463.     OSStatus         err = noErr;
  1464.     UTDriverGlobals    *ourPB;
  1465.  
  1466.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1467.     switch (ourPB->currentExecutionState )
  1468.     {
  1469.         case kEjectStartState:
  1470.         {
  1471.             ourPB->currentExecutionState = kEjectAllowRemovalDone;
  1472.             err = PreventAllowRemoval(ourPB, false, &EjectTheCartridge); // Allow for Ejects
  1473.         }
  1474.         break;
  1475.         
  1476.         case kEjectAllowRemovalDone:
  1477.         {
  1478.             ourPB->currentExecutionState = kEjectCartridgeDone;
  1479.             EjectCartridge(&gTheUTGlobals, EjectTheCartridge);
  1480.         }
  1481.         break;
  1482.         
  1483.         case kEjectCartridgeDone:
  1484.         {
  1485.             AbsoluteTime            theWait;
  1486.             StorageExecuteCommandPB    *commandPB;
  1487.             DriveRec                *drive;
  1488.         
  1489.             drive = &ourPB->theDrive;
  1490.             RemoveDrive(drive);
  1491.             commandPB = &ourPB->drivePB.executePB;                          // use a pointer to the executePB field
  1492.             ourPB->drivePB.status = commandPB->status;
  1493.             if(commandPB->status == noErr)
  1494.             {
  1495.                 ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1496.                 theWait = DurationToAbsolute(durationSecond);
  1497.                 theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1498.                 SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1499.             }
  1500.             
  1501.             drive->capacity = 0;
  1502.             drive->blockSize = 0;
  1503.             ourPB->isFloppy = false;
  1504.             ourPB->diskInDrive = false;
  1505.         }
  1506.         break;
  1507.     }
  1508. }
  1509.  
  1510. OSStatus FormatCompletionInterrupt( void *p1, void *p2)
  1511. {
  1512. #pragma unused ( p1, p2 )
  1513.     gInterruptTimer = 0;
  1514.     gTheUTGlobals.currentExecutionState = kFormatWaitDoneState;
  1515.     FormatTheCartridge( &gTheUTGlobals );
  1516.     return noErr;
  1517. }
  1518.  
  1519. void FormatTheCartridge( void *theCurrentPB )
  1520. {
  1521.     OSStatus         err = noErr;
  1522.     AbsoluteTime    oneSecondWait;
  1523.     DriveRec        *drive;
  1524.     UTDriverGlobals    *ourPB;
  1525.  
  1526.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1527.     drive = &ourPB->theDrive;
  1528.     oneSecondWait = DurationToAbsolute(durationSecond);
  1529.  
  1530.     switch (ourPB->currentExecutionState )
  1531.     {
  1532.         case kFormatStartState:
  1533.         {
  1534.             ourPB->currentExecutionState = kFormatDoneState;
  1535.             err = FormatFloppyCartridge( ourPB, &FormatTheCartridge);
  1536.         }
  1537.         break;
  1538.         
  1539.         case kFormatDoneState:
  1540.         {
  1541.             AbsoluteTime    theWait;
  1542.             
  1543.             theWait = DurationToAbsolute(durationSecond*35);
  1544.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1545.             SetInterruptTimer( &theWait, &FormatCompletionInterrupt, nil, &gInterruptTimer);
  1546.             err = 1;
  1547.         }
  1548.         break;
  1549.         
  1550.         case kFormatWaitDoneState:
  1551.         {
  1552.             ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1553.             ourPB->drivePB.retryCount = 0;
  1554.             err = RequestSense( ourPB, &FormatTheCartridge);
  1555.         }
  1556.         break;
  1557.         
  1558.         case kFormatRequestSenseDoneState:
  1559.         {
  1560.             //SysDebugStr("\pRequest is done");
  1561.             if(ourPB->drivePB.executePB.status == noErr)
  1562.             {
  1563.                 if(ourPB->sense[12] == 0x00)
  1564.                 {
  1565.                     ourPB->currentExecutionState = kFormatGetGeometryDoneState;
  1566.                     ourPB->getCapacity.lastLogicalBlock = 0;
  1567.                     ourPB->getCapacity.blockLength = 0;
  1568.                     err = GetMediaGeometry( ourPB, &FormatTheCartridge);
  1569.                 }
  1570.                 else
  1571.                 {
  1572.                     // The format has failed, inform the OS
  1573.                     err = controlErr;
  1574.                 }
  1575.             }
  1576.             else
  1577.             {
  1578.                 if (ourPB->drivePB.retryCount < kFormatRequestSenseRetryCount)
  1579.                 {
  1580.                     ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1581.                     ourPB->drivePB.retryCount += 1;
  1582.                     err = RequestSense( ourPB, &FormatTheCartridge);
  1583.                 }
  1584.                 else
  1585.                 {
  1586.                     // The format has failed, inform the OS
  1587.                     ourPB->drivePB.retryCount = 0;
  1588.                     err = controlErr;
  1589.                 }
  1590.             }
  1591.         }
  1592.         break;
  1593.  
  1594.         case kFormatGetGeometryDoneState:
  1595.         {
  1596.             ReadCapacityData        *getGeometry;
  1597.             
  1598.             getGeometry = (ReadCapacityData *) &ourPB->getCapacity;
  1599.  
  1600.             drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in
  1601.             drive->blockSize = getGeometry->blockLength;
  1602.             
  1603.             if( (ourPB->drivePB.executePB.status != noErr) || (drive->capacity == 0) || (drive->blockSize == 0))
  1604.             {
  1605.                 // The format has failed, inform the OS
  1606.                 err = controlErr;
  1607.                 break;
  1608.             }
  1609.  
  1610.             if(    drive->capacity >0x0C00)
  1611.             {
  1612.                 ourPB->isFloppy = false;
  1613.             }
  1614.             else
  1615.             {
  1616.                 ourPB->isFloppy = true;
  1617.             }
  1618.  
  1619.             ourPB->diskInDrive = true;
  1620.         }
  1621.         break;
  1622.     }
  1623.     
  1624.     ourPB->drivePB.status = err;
  1625.     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  1626. }
  1627.  
  1628.  
  1629. OSStatus CheckWriteProtect(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1630. {
  1631.     OSStatus    status = noErr;
  1632.     
  1633.     IfDebugging("\pCheck Write Protect");
  1634.  
  1635.     if (gItsTheDispatchTable)
  1636.     {
  1637.         StorageExecuteCommandPB    *commandPB;
  1638.         
  1639.         commandPB = &theCurrentPB->drivePB.executePB;                      // use a pointer to the executePB field
  1640.         
  1641.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));            // clear out the PB we will send
  1642.         BlockZero((Ptr) &theCurrentPB->sense[0], 8);
  1643.  
  1644.         commandPB->cdb[0] =            kCmdModeSense;
  1645.         commandPB->cdb[8] =            0x08;
  1646.         commandPB->flags =             kStorageDataIn;                                    // -> Expect a data in transfer
  1647.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];                    // -> Pointer to user buffer
  1648.         commandPB->expectedCount =    8;                                                // -> Expected number of bytes to transfer
  1649.         commandPB->completionProc =    ourCompletion;                                    // -> Completion routine
  1650.         
  1651.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1652.     }
  1653.     
  1654.     return status;
  1655. }
  1656.  
  1657. OSStatus TUR(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1658. {
  1659.     OSStatus    status = noErr;
  1660.     
  1661.     IfDebugging("\pTUR");
  1662.  
  1663.     if (gItsTheDispatchTable)
  1664.     {
  1665.         StorageExecuteCommandPB    *commandPB;
  1666.         
  1667.         commandPB = &theCurrentPB->drivePB.executePB;              // use a pointer to the executePB field
  1668.         
  1669.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));    // clear out the PB we will send
  1670.     
  1671.         commandPB->flags =             kStorageNoData;    
  1672.         commandPB->completionProc =    ourCompletion;
  1673.         
  1674.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1675.     }
  1676.     
  1677.     return status;
  1678. }
  1679.  
  1680. OSStatus RequestSense(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1681. {
  1682.     OSStatus                status = noErr;
  1683.  
  1684.     if (gItsTheDispatchTable)
  1685.     {
  1686.         StorageExecuteCommandPB    *commandPB;
  1687.         
  1688.         commandPB = (StorageExecuteCommandPB *)&theCurrentPB->drivePB.executePB;              // use a pointer to the executePB field
  1689.  
  1690.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1691.         BlockZero((Ptr) &theCurrentPB->sense[0], sizeof(kSenseDataSize));
  1692.         
  1693.         commandPB->cdb[0] =            kCmdRequestSense;
  1694.         commandPB->cdb[4] =            kSenseDataSize;
  1695.         commandPB->flags =             kStorageDataIn;                            // -> Expect a data in transfer
  1696.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];            // -> Pointer to user buffer
  1697.         commandPB->expectedCount =    kSenseDataSize;                            // -> Expected number of bytes to transfer
  1698.         commandPB->completionProc =    ourCompletion;                            // -> Completion routine
  1699.         
  1700.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1701.     }
  1702.     
  1703.     return status;
  1704. }
  1705.  
  1706. OSStatus GetMediaGeometry(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1707. {
  1708.     OSStatus    status = noErr;
  1709.  
  1710.     IfDebugging("\pGetMediaGeometry");
  1711.  
  1712.     if (gItsTheDispatchTable)
  1713.     {
  1714.         ReadCapacityData        *getGeometry;
  1715.         StorageExecuteCommandPB    *commandPB;
  1716.         
  1717.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1718.         getGeometry = &theCurrentPB->getCapacity;
  1719.         
  1720.         getGeometry->lastLogicalBlock = 0;
  1721.         getGeometry->blockLength = 0;
  1722.         
  1723.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1724.  
  1725.         commandPB->cdb[0] =            kCmdReadCapacity;
  1726.         commandPB->flags =             kStorageDataIn;                            // -> Expect a data in transfer
  1727.         commandPB->userBuffer =        (Ptr) getGeometry;                        // -> Pointer to user buffer
  1728.         commandPB->expectedCount =    sizeof(ReadCapacityData);                // -> Expected number of bytes to transfer
  1729.         commandPB->completionProc =    ourCompletion;                            // -> Completion routine
  1730.         
  1731.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1732.     }
  1733.  
  1734.     return status;
  1735. }
  1736.  
  1737.  
  1738. OSStatus ReadFormatCapacity(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1739. {
  1740.     OSStatus    status = noErr;
  1741.  
  1742.     IfDebugging("\pReadFormatCapacity");
  1743.  
  1744.     if (gItsTheDispatchTable)
  1745.     {
  1746.         StorageExecuteCommandPB    *commandPB;
  1747.         
  1748.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1749.  
  1750.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1751.  
  1752.         commandPB->cdb[0] =            kCmdReadFormatCapacities;
  1753.         commandPB->cdb[7] =            0;
  1754.         commandPB->cdb[8] =            0x0C;
  1755.         commandPB->flags =             kStorageDataIn;                                    // -> Expect a data in transfer
  1756.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];                    // -> Pointer to user buffer
  1757.         commandPB->expectedCount =    0x0C;                                            // -> Expected number of bytes to transfer
  1758.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1759.         
  1760.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1761.     }
  1762.  
  1763.     return status;
  1764. }
  1765.  
  1766.  
  1767. OSStatus FormatFloppyCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1768. {
  1769.     OSStatus    status = noErr;
  1770.  
  1771.     IfDebugging("\pFormatFloppyCartridge");
  1772.  
  1773.     if (gItsTheDispatchTable)
  1774.     {
  1775.         StorageExecuteCommandPB    *commandPB;
  1776.  
  1777.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1778.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1779.         BlockZero((Ptr) &theCurrentPB->sense[0], 0x0C);
  1780.  
  1781.         // Set up the data going out
  1782.         // First set up the Defect List Header
  1783.         theCurrentPB->sense[0] = 0;
  1784.         theCurrentPB->sense[1] = 0;
  1785.         theCurrentPB->sense[2] = 0;
  1786.         theCurrentPB->sense[3] = 0x08;
  1787.  
  1788.         *((UInt32 *) &theCurrentPB->sense[4]) = theCurrentPB->theDrive.capacity;
  1789.         *((UInt16 *) &theCurrentPB->sense[10]) = theCurrentPB->theDrive.blockSize;
  1790.         
  1791.         commandPB->cdb[0] =            kCmdFormat;
  1792.         commandPB->cdb[1] =            0x17;
  1793.         commandPB->flags =             kStorageDataOut;                    // -> Expect a data out transfer
  1794.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];        // -> Pointer to user buffer
  1795.         commandPB->expectedCount =    0x0C;                                // -> Expected number of bytes to transfer
  1796.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1797.  
  1798.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1799.     }
  1800.  
  1801.     return status;
  1802. }
  1803.  
  1804.  
  1805. OSStatus EjectCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1806. {
  1807.     OSStatus    status = noErr;
  1808.  
  1809.     if (gItsTheDispatchTable)
  1810.     {
  1811.         StorageExecuteCommandPB    *commandPB;
  1812.         
  1813.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1814.  
  1815.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1816.         
  1817.         commandPB->cdb[0] =            kCmdStartStopUnit;
  1818.         commandPB->cdb[4] =            0x02;                            // Unload the Media
  1819.         commandPB->flags =             kStorageNoData;                    // -> Expect a data in transfer
  1820.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  1821.         commandPB->expectedCount =    0;                                // -> Expected number of bytes to transfer
  1822.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1823.         
  1824.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1825.     }
  1826.  
  1827.     return status;
  1828. }
  1829.  
  1830.  
  1831. OSStatus PreventAllowRemoval(UTDriverGlobals *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion)
  1832. {
  1833.     OSStatus    status = noErr;
  1834.  
  1835.     if (gItsTheDispatchTable)
  1836.     {
  1837.         StorageExecuteCommandPB    *commandPB;
  1838.         
  1839.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1840.  
  1841.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1842.         
  1843.         commandPB->cdb[0] =            kCmdPreventAllowRemoval;
  1844.         if ( preventRemoval == true )
  1845.         {
  1846.             commandPB->cdb[4] =            0x01;                        // Prevent Media Removal
  1847.         }
  1848.         else
  1849.         {
  1850.             commandPB->cdb[4] =            0x00;                        // Allow Media Removal
  1851.         }
  1852.         
  1853.         commandPB->flags =             kStorageNoData;                    // -> Expect a data in transfer
  1854.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  1855.         commandPB->expectedCount =    0;                                // -> Expected number of bytes to transfer
  1856.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1857.         
  1858.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1859.     }
  1860.  
  1861.     return status;
  1862. }
  1863.  
  1864.  
  1865. /* ---------------------Supporting Functions below this point -------------------------- */
  1866. //------------------------------------------------------------------------------
  1867. //    Function:        DRVRPrime
  1868. //    Description:    This is the ATA driver PRIME call that performs
  1869. //                    reading and writing to the device.
  1870. //                    
  1871. //    Input:            ioPB = Pointer to caller's I/O parameter block
  1872. //                    dce = Pointer to Device Control Entry (DCE)
  1873. //    Output:            A status code is returned.
  1874. //-------------------------------------------------------------------------------
  1875. OSStatus DoReadWriteCommand( UTDriverGlobals *theDriverPB, Boolean doWrite )
  1876. {
  1877.     OSStatus        err;
  1878.     UInt32            startingBlock, numBlocks;
  1879.     VolumeRecPtr    vol;
  1880.     IOParamPtr         iopb;
  1881.     DriveRecPtr     drive;
  1882.     
  1883.     drive = &theDriverPB->theDrive;
  1884.     iopb = (IOParamPtr) theDriverPB->drivePB.theIOPB;
  1885.  
  1886.     // Find the associated volume and drive records required for the request.
  1887.     vol = GetVolume(drive, iopb->ioVRefNum, 0);            // Get the volume record requested 
  1888.     
  1889.     //if (vol)                                        // if we have a volume
  1890.     //    drive = (DriveRecPtr)vol->drivePtr;            // get its drive record
  1891.     //else                // assume request if for a physical drive
  1892.     if (!vol)                                        // if we have a volume
  1893.     {
  1894.         if (drive)                                    // if physical drive matches…
  1895.         {
  1896.             vol = drive->nextVol;
  1897.             if (vol)                                // and a volume exists for it…
  1898.             {
  1899.                 if (vol->curoffset)                    // and not doing physical addressing…
  1900.                 {
  1901.                     vol = 0;                        // the volume is invalid
  1902.                 }
  1903.             }
  1904.         }
  1905.     }
  1906.     
  1907.     if (!vol || !drive)                // Abort if we don't have both drive and volume records
  1908.         return(nsDrvErr);
  1909.         
  1910.     //................................................................................
  1911.     // A usable drive and volume exists.  Continue processing the request…
  1912.  
  1913.     // Compute the starting block address for the request.  We accept
  1914.     // only the normal 32 bit address and not the new 'Large Volume (64 bit) Addressing'.
  1915.     //SysDebugStr("\pCalculate the Read.");
  1916.     startingBlock = (UInt32)((iopb->ioPosOffset)/(drive->blockSize));
  1917.     numBlocks = (iopb->ioReqCount)/(drive->blockSize);
  1918.  
  1919.     if (doWrite && vol->driveStatus.writeProt)
  1920.     {
  1921.         // check for write protect
  1922.         err = wPrErr;
  1923.     }
  1924.     else if (iopb->ioReqCount & ((drive->blockSize) - 1))
  1925.     {
  1926.         // Verify if request is a multiple of the drives blocksize
  1927.         err = paramErr;
  1928.     }
  1929.     else if ( ( startingBlock + numBlocks ) > ((vol->curoffset) ? vol->partblks : drive->capacity))
  1930.     {
  1931.         // Verify if request is within range with respect to doing physical or logical I/O.
  1932.         // Access is limited to partition range (logical I/O) if curoffset is non-zero.
  1933.         err = paramErr;
  1934.     }
  1935.     else if ((iopb->ioPosMode & rdVerify) && !doWrite)
  1936.     {
  1937.         // If Read-Verify mode, this is not efficient with disks so we simply pretend we did it.
  1938.         iopb->ioActCount = iopb->ioReqCount;
  1939.         iopb->ioPosOffset += iopb->ioActCount;
  1940.         err = noErr;
  1941.     } 
  1942.     else                                    // Do the read or write
  1943.     {
  1944.         startingBlock += vol->curoffset;            // add in the partition offset
  1945.         err = ReadWriteBlock( theDriverPB, startingBlock, numBlocks, iopb->ioBuffer, doWrite, true );
  1946.     }
  1947.  
  1948.     return err;
  1949. }
  1950.  
  1951. //------------------------------------------------------------------------------
  1952. //    Function:        ReadWriteBlock
  1953. //
  1954. //    Description:    Low level read/write block on the media with retries.
  1955. //
  1956. //    Input:            drive:            Pointer to physical drive record
  1957. //                        blockAddr:        Starting block address 
  1958. //                        numBlocks:        Number of blocks to read/write
  1959. //                        buffer:            Pointer to buffer
  1960. //                        doWrite:            1 = write, 0 = read
  1961. //
  1962. //    Output:            true if successful, false if not
  1963. //-------------------------------------------------------------------------------
  1964. OSStatus ReadWriteBlock( UTDriverGlobals *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync)
  1965. {    
  1966.     StorageExecuteCommandPBPtr     theReadWriteRequest;
  1967.     UInt32                        driveBlockSize;
  1968.     volatile OSStatus            status = noErr;
  1969.  
  1970.     IfDebugging("\p…ReadWriteBlock");
  1971.     theDriverPB->drivePB.status = noErr;
  1972.     driveBlockSize = theDriverPB->theDrive.blockSize;
  1973.     theReadWriteRequest = &(theDriverPB->drivePB.executePB);
  1974.  
  1975.     BlockZero(theReadWriteRequest, sizeof(StorageExecuteCommandPB));    // clear out the PB we will send
  1976.     theReadWriteRequest->userBuffer = buffer;                // -> Pointer to user buffer
  1977.     theReadWriteRequest->expectedCount = numBlocks*driveBlockSize;    // -> Expected number of bytes to transfer
  1978.  
  1979.     theReadWriteRequest->completionProc = (StorageClassCompletionProcPtr) ReadWriteCompletion;    // -> Completion routine
  1980.     theReadWriteRequest->actualCount = 0;                    // <- Actual number of bytes transferred
  1981.     theReadWriteRequest->status = 0;                    // <- Result of operation
  1982.  
  1983.     if(doWrite)
  1984.     {
  1985.         theReadWriteRequest->cdb[0] = kCmdWrite;
  1986.         theReadWriteRequest->flags     = kStorageDataOut;        // -> Expect a data out transfer
  1987.     }
  1988.     else
  1989.     {
  1990.         theReadWriteRequest->cdb[0] = kCmdRead;
  1991.         theReadWriteRequest->flags     = kStorageDataIn;        // -> Expect a data in transfer
  1992.     }
  1993.     
  1994.     // Set the starting block in the CDB
  1995.     theReadWriteRequest->cdb[2] = (startBlock >> 24) & 0xff;
  1996.     theReadWriteRequest->cdb[3] = (startBlock >> 16) & 0xff;
  1997.     theReadWriteRequest->cdb[4] = (startBlock >> 8) & 0xff;
  1998.     theReadWriteRequest->cdb[5] = startBlock & 0xff;
  1999.     
  2000.     // Set the Block Count in the CDB
  2001.     theReadWriteRequest->cdb[7] = (numBlocks >> 8) & 0xff;
  2002.     theReadWriteRequest->cdb[8] = numBlocks & 0xff;
  2003.  
  2004.     status = ((gItsTheDispatchTable)->pStorageExecuteCmd)( theReadWriteRequest );            
  2005.  
  2006.     theDriverPB->drivePB.status = status;
  2007.  
  2008.     if(status != 1)
  2009.     {
  2010.         theDriverPB->drivePB.theIOPB = nil;
  2011.     }
  2012.     else
  2013.     {
  2014.         if(doAsync == false)
  2015.         {
  2016.             while ( status == 1 )
  2017.             {
  2018.                 status = theDriverPB->drivePB.status;
  2019.             }
  2020.         }
  2021.     }
  2022.     
  2023.     return status;
  2024. }
  2025.  
  2026.  
  2027. void ReadWriteCompletion( void *theDriverPB )
  2028. {
  2029.     OSStatus            status;
  2030.     UTDriverGlobals        *ourPB;
  2031.     Boolean             wasWrite;
  2032.     
  2033.     //SysDebugStr("\pReadWriteCompletion;g");
  2034.     ourPB = (UTDriverGlobals *) theDriverPB;
  2035.     status = ourPB->drivePB.executePB.status;
  2036.     wasWrite = ((ourPB->drivePB.executePB.flags & kStorageDataOut) == kStorageDataOut);
  2037.     if(ourPB->doInternalReadWrite == false)
  2038.     {
  2039.         IOParamPtr         iopb;
  2040.  
  2041.         iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2042.         
  2043.         if( status == noErr )
  2044.         {
  2045.             if( wasWrite == true )
  2046.             {
  2047.                 // It was a write, do a Request sense to determine if any 
  2048.                 // errors occurred.
  2049.                 status = RequestSense( ourPB, &WriteRequestSenseCompletion);
  2050.                 
  2051.                 if( status != 1)
  2052.                 {
  2053.                     // An error occurred while trying to do the Request sense,
  2054.                     // return an ioErr to the system.
  2055.                     iopb->ioActCount = 0;
  2056.                     
  2057.                     // Set the status in the DriverPB last, this way if there is an immediate command,
  2058.                     // It won't think the command is done till after our processing.
  2059.                     ourPB->drivePB.status = ioErr;
  2060.                     
  2061.                     // Signal completion of the command to the operating system
  2062.                     ourPB->drivePB.theIOPB = nil;
  2063.                     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2064.                 }
  2065.             }
  2066.             else
  2067.             {
  2068.                 // This was a read command, and no errors occurred, finish the IO request and
  2069.                 // return to the system.
  2070.                 iopb->ioActCount = iopb->ioReqCount;
  2071.                 
  2072.                 // Set the status in the DriverPB last, this way if there is an immediate command,
  2073.                 // It won't think the command is done till after our processing.
  2074.                 ourPB->drivePB.status = noErr;
  2075.                 
  2076.                 // Signal completion of the command to the operating system
  2077.                 ourPB->drivePB.theIOPB = nil;
  2078.                 FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2079.             }
  2080.         }
  2081.         else
  2082.         {
  2083.             // An error occurred on the command, do a Request sense to be certain any
  2084.             // USB Device stalls are cleared.
  2085.             status = RequestSense( ourPB, &RequestSenseOnErrorCompletion);
  2086.             
  2087.             if( status != 1)
  2088.             {
  2089.                 // An error occurred while trying to do the Request sense,
  2090.                 // return an ioErr to the system.
  2091.                 iopb->ioActCount = 0;
  2092.                 
  2093.                 // Set the status in the DriverPB last, this way if there is an immediate command,
  2094.                 // It won't think the command is done till after our processing.
  2095.                 ourPB->drivePB.status = ioErr;
  2096.                 
  2097.                 // Signal completion of the command to the operating system
  2098.                 ourPB->drivePB.theIOPB = nil;
  2099.                 FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2100.             }
  2101.         }
  2102.     }
  2103.     else
  2104.     {
  2105.         // If this is an internal command, this is all we care about
  2106.         ourPB->drivePB.status = status;
  2107.     }
  2108.  
  2109.     IfDebugging("\p…ReadWriteBlock Done");
  2110. }
  2111.  
  2112. void WriteRequestSenseCompletion( void *theDriverPB )
  2113. {
  2114.     OSStatus        status;
  2115.     UTDriverGlobals    *ourPB;
  2116.     IOParamPtr         iopb;
  2117.     
  2118.     ourPB = (UTDriverGlobals *) theDriverPB;
  2119.     status = ourPB->drivePB.executePB.status;
  2120.     iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2121.  
  2122.     if( status == noErr )
  2123.     {
  2124.         if( ( ourPB->sense[2] & 0x0F !=0 ) && ( ourPB->sense[2] & 0x0F !=1 ))
  2125.         {
  2126.             // An error has been reported back in the sense key, return 
  2127.             // an ioErr to the system.
  2128.             iopb->ioActCount = 0;
  2129.             ourPB->drivePB.status = ioErr;
  2130.         }
  2131.         else
  2132.         {
  2133.             // No errors has been reported back in the sense key, return 
  2134.             // a noErr to the system.
  2135.             iopb->ioActCount = iopb->ioReqCount;
  2136.             ourPB->drivePB.status = noErr;
  2137.         }
  2138.     }
  2139.     else
  2140.     {
  2141.         // Errors occurred on the Request sense report an
  2142.         // ioErr back to the system
  2143.         iopb->ioActCount = 0;
  2144.         ourPB->drivePB.status = ioErr;
  2145.     }
  2146.  
  2147.     // Signal completion of the command to the operating system
  2148.     //SysDebugStr("\pWriteReqSenseFinishCommand");
  2149.     ourPB->drivePB.theIOPB = nil;
  2150.     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2151. }
  2152.  
  2153. void RequestSenseOnErrorCompletion( void *theDriverPB )
  2154. {
  2155.     UTDriverGlobals    *ourPB;
  2156.     IOParamPtr         iopb;
  2157.     
  2158.     ourPB = (UTDriverGlobals *) theDriverPB;
  2159.     iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2160.         
  2161.     // An error occurred on the initial command, and no error occurred on the RequestSense
  2162.     if( (ourPB->drivePB.retryCount < kReadWriteRetryCount) && (ourPB->drivePB.executePB.status == noErr))
  2163.     {
  2164.         OSStatus err;
  2165.         // We have not yet exceeded the retry count, so try the operation again
  2166.             // Increment our retry counter
  2167.             ourPB->drivePB.retryCount += 1;
  2168.     
  2169.             // Send the command out again
  2170.             err = DoReadWriteCommand( ourPB, ourPB->drivePB.doWrite);
  2171.         
  2172.         if ( err !=1 )
  2173.         {
  2174.             iopb->ioActCount = 0;
  2175.             ourPB->drivePB.status = ioErr;
  2176.         
  2177.             // Signal completion of the command to the operating system
  2178.             ourPB->drivePB.theIOPB = nil;
  2179.             FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2180.         }
  2181.     }
  2182.     else
  2183.     {
  2184.         // Since we only get here if an error occurred on the orignal
  2185.         // command, and the Request Sense was to clear any device stalls.
  2186.         // We will always report back an ioErr to the system.
  2187.         iopb->ioActCount = 0;
  2188.         ourPB->drivePB.status = ioErr;
  2189.     
  2190.         // Signal completion of the command to the operating system
  2191.         ourPB->drivePB.theIOPB = nil;
  2192.         FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2193.     }
  2194. }
  2195.  
  2196.  
  2197. //------------------------------------------------------------------------------
  2198. //    Function:        GetVolume
  2199. //
  2200. //    Description:    Searches for the volume record with the specified vRefNum and 
  2201. //                    returns its VolumeRecPtr.  Optionally, partitionNum
  2202. //                    can be use as search keys if vRefNum is zero.
  2203. //
  2204. //    Input:            drive:            the pointer to the DriveRec
  2205. //                    vRefNum:        the volume's reference number
  2206. //                    partitionNum:    the volume's partition number
  2207. //
  2208. //    Output:            pointer to volume record if found, nil if not found
  2209. //-------------------------------------------------------------------------------
  2210. VolumeRecPtr GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum)
  2211. {
  2212.     VolumeRecPtr vol = nil;
  2213.     Boolean found;
  2214.     
  2215.     // Search all volume records for one matching the search key
  2216.  
  2217.     vol = drive->nextVol;                        // first volume pointer
  2218.  
  2219.     while (vol)
  2220.     {
  2221.         found = (vRefNum) ? (vol->vRefNum == vRefNum) : (vol->partitionNo == partitionNum);
  2222.  
  2223.         if (found)
  2224.             return(vol);                        // found the partition
  2225.         else
  2226.             vol = (VolumeRecPtr)vol->nextVol;    // otherwise, get next volume pointer
  2227.     }
  2228.     
  2229.     return(vol);                                // nil if volume not found
  2230. }
  2231.  
  2232.  
  2233. //------------------------------------------------------------------------------
  2234. //    Function:        CreateVolume
  2235. //
  2236. //    Description:    This function creates a volume record for the specified drive.
  2237. //                        The volume is appended to the drive's volume queue and a logical
  2238. //                        logical drive is installed in the system drive queue.  It is 
  2239. //                        assumed the volume is not write protected, drive is installed,
  2240. //                        and the media is installed.  
  2241. //                    
  2242. //    Input:            drive:            pointer to the drive record of the volume to create
  2243. //                    partitionID:    the volume's partition ID
  2244. //                    volSize:        size of the volume in blocks
  2245. //                    volOffset:        block offset of volume on drive
  2246. //
  2247. //    Output:            Returns nil pointer if fails, else pointer to volume record
  2248. //
  2249. //    NOTE:            Assumes all inputs are valid!
  2250. //-------------------------------------------------------------------------------
  2251. VolumeRecPtr CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset )
  2252. {
  2253.     register VolumeRecPtr        vol;
  2254.     VolumeRecPtr*                volHandle;
  2255.     Boolean                        volInQueue = false;
  2256.     
  2257.     // When a volume is offlined by the system (greyed desktop icon) it is still
  2258.     // being used and its associated DrvQEl must remain.  Thus, its volume and 
  2259.     // physical drive record also must remain.  So, when new media is inserted 
  2260.     // we need to check for these structures and reuse them before creating new ones.
  2261.     // The structures can be reused since the file system verifies the volume.
  2262.  
  2263.     if( drive->nextVol == nil)
  2264.     {
  2265.         // Make sure the drive's volume queue points to the volume
  2266.         vol = &gTheUTGlobals.theVolume;
  2267.     }
  2268.     else
  2269.     {
  2270.         // Search for a volume record for the volume (partition) to be created…
  2271.         vol = GetVolume(drive, 0, partitionID);
  2272.         
  2273.         if (vol)                                                        // if record exists for this volume…
  2274.         {
  2275.             volInQueue = true;                                            // remember volume is already in queue
  2276.     
  2277.             if (vol->driveStatus.diskInPlace)                            // if volume is already online…
  2278.                 vol = nil;                                                // we shouldn't be here - fall thru
  2279.         }
  2280.         else                                                            // need to create volume record
  2281.         {
  2282.             vol = (VolumeRecPtr) PoolAllocateResident(sizeof(VolumeRec), true);    // Allocate storage for record
  2283.         }
  2284.     }
  2285.     
  2286.     if (vol)                                                        // if a volume record is valid…
  2287.     {    
  2288.         if( gTheUTGlobals.isFloppy == true)
  2289.         {
  2290.             vol->driveStatus.track             = 80;            // Sectors on a MFM disk
  2291.             vol->driveStatus.sides             = -1;            // -1 means double sided floppy
  2292.         }
  2293.         else
  2294.         {
  2295.             vol->driveStatus.track             = 0;            // Only used on floppys
  2296.             vol->driveStatus.sides             = 0;            // Only used on floppys
  2297.         }        
  2298.  
  2299.         if( gTheUTGlobals.isWriteProtected == true)
  2300.         {
  2301.             vol->driveStatus.writeProt     = 0x80;            // disk is write protected
  2302.         }
  2303.         else
  2304.         {
  2305.             vol->driveStatus.writeProt     = 0;            // not write protected yet
  2306.         }
  2307.         vol->driveStatus.diskInPlace     = 1;            // Ejectable Disk
  2308.         vol->driveStatus.installed         = 1;            // drive is installed
  2309.         vol->driveStatus.dQFSID         = 0;                        // File Manager's volume type
  2310.         if( gTheUTGlobals.isFloppy == true)
  2311.         {
  2312.             DrvSts    *theDriveStatus;
  2313.             
  2314.             theDriveStatus = (DrvSts *) &vol->driveStatus;
  2315.             theDriveStatus->qType         = 0;                        // This is a floppy drive status structure
  2316.             theDriveStatus->twoSideFmt = -1;                        // after 1st rd/wrt: 0=1 side, -1=2 side
  2317.             theDriveStatus->needsFlush = -1;                        // -1 for MacPlus drive
  2318.             theDriveStatus->diskErrs = 0;                            // soft error count
  2319.  
  2320.             vol->mediaIconPtr =    (Ptr) AppleFloppyMediaIcon;        // media icon for floppies
  2321.         }
  2322.         else
  2323.         {
  2324.             vol->driveStatus.qType             = 1;                    // both dQDrvSz and dQDrvSz2 are used
  2325.             vol->driveStatus.driveSize        = (UInt16) volSize;        // volume size in blocks
  2326.             vol->driveStatus.driveS1         = (UInt16) (volSize >> 16);
  2327.             
  2328.             vol->mediaIconPtr =    (Ptr) &CartridgeIcon;                    // media icon for cartridge
  2329.         }
  2330.             
  2331.  
  2332.         vol->mountthispart =    false;                                // don't mount this volume,
  2333.         vol->partmounted =        false;                                // it's not mounted yet,
  2334.         vol->partitionNo =        partitionID;                        // save the partition ID
  2335.  
  2336.         // Save volume's block offset and set its access mode by setting curoffset to the
  2337.         // same (access is relative to partition offset if curoffset is non-zero, else physical)
  2338.         vol->curoffset =        volOffset;                            // block offset of volume
  2339.         vol->partoffset =        volOffset;                            // partition offset
  2340.         vol->partblks =            volSize;                            // save size for our use also
  2341.         vol->drivePtr =        (Ptr) drive;                            // pointer to vol's physical drive
  2342.         // If this record is not in our volume list yet, finish initializing and insert
  2343.         if (!volInQueue)                                            // If not in the volume queue…
  2344.         {
  2345.             vol->driveStatus.qLink = nil;                            // initialize system queue link
  2346.             vol->nextVol = nil;                                        // no link to next volume yet
  2347.  
  2348.             // Find the end of the drive's volume queue and link in the new volume record.
  2349.             volHandle = &(drive->nextVol);                            // point to start of drive's volume queue
  2350.             while (*volHandle)                                        // search for end of volume queue…
  2351.             {
  2352.                 volHandle = (VolumeRecPtr*)&((*volHandle)->nextVol);
  2353.             }
  2354.             
  2355.             *volHandle = vol;                                        // at end of queue, insert volume
  2356.         }
  2357.  
  2358.         drive->numVolumes++;                                        // update number of drive volumes
  2359.     }
  2360.     return(vol);
  2361. }
  2362.  
  2363.  
  2364. //------------------------------------------------------------------------------
  2365. //    Function:        InstallVolumes
  2366. //    Description:    Searches for partitions on the media and installs them as
  2367. //                        volumes for the associated drive.  Assumes the drive does
  2368. //                        not have any volumes installed yet. 
  2369. //
  2370. //    Input:            theDrive:    pointer to drive record
  2371. //                        mountVols:    true means to mount the drive's partitions
  2372. //
  2373. //-------------------------------------------------------------------------------
  2374. void InstallVolumes(DriveRecPtr theDrive, Boolean mountVols)
  2375. {
  2376.     VolumeRecPtr    vol = nil;
  2377.  
  2378.     // If the driver is going to support partitions, this is were the media should be 
  2379.     // scanned to see if there are any valid HFS or DOS partitions.
  2380.     
  2381.     // If no valid paritions are found, or partitions are not support, the following code
  2382.     // will create a volume of the entire media capacity, post a disk inserted
  2383.     // event and let the File System Manager try and figure it out.  Note we post a disk
  2384.     // inserted event rather than notifying FSM so if the media is not recognized (because
  2385.     // it's unformatted or the correct file system is not installed) the system will prompt
  2386.     // with a "This is not a Macintosh disk…" message.  If we call FSM instead, the user will
  2387.     // not be prompted when the media is unformatted.  This provides a way to format media.
  2388.  
  2389.     if (theDrive->numVolumes == 0)                            // if no partitions were found, or are not supported
  2390.     {
  2391.         IfDebugging("\ptheDrive->numVolumes == 0");
  2392.         vol = CreateVolume(theDrive, 1, theDrive->capacity, 0);
  2393.         if (vol)
  2394.         {
  2395.             vol->mountthispart = true;                                    // post disk inserted event later
  2396.         }
  2397.     }
  2398.  
  2399.     // Add the remaining volumes to the drive queue…
  2400.     vol = theDrive->nextVol;                                            // first volume of the drive
  2401.     while (vol)
  2402.     {
  2403.         if ((vol->mountthispart == true) || (mountVols == false))
  2404.         {
  2405.             UInt16 volNumber;
  2406.             
  2407.             vol->vRefNum = NextQDrive();        // assign a logical drive number
  2408.             volNumber = vol->vRefNum;
  2409.             
  2410.             NativeAddDrive(gTheUTGlobals.drvrRefNum, volNumber,(DrvQElPtr)  &vol->driveStatus.qLink);
  2411.         }
  2412.         
  2413.         vol = (VolumeRecPtr)vol->nextVol;                            // point to the next volume
  2414.     }
  2415. }
  2416.  
  2417.  
  2418. //------------------------------------------------------------------------------
  2419. //    Function:        RemoveVolume
  2420. //    Description:    Deletes a volume and its record
  2421. //    Input:            drvRec:        pointer to physical drive record of volume
  2422. //                    volRef:        Volume reference
  2423. //-------------------------------------------------------------------------------
  2424. void RemoveVolume(DriveRecPtr drvRec, UInt16 volRef)
  2425. {
  2426.     VolumeRecPtr*    pvHandle = &(drvRec->nextVol);
  2427.     VolumeRecPtr    pvPtr = *pvHandle;
  2428.     
  2429.     while (pvPtr) 
  2430.     {
  2431.         if (pvPtr->vRefNum == volRef)             // found the volume record
  2432.         {        
  2433.             Dequeue((QElemPtr) &(pvPtr->driveStatus.qLink), GetDrvQHdr());    // remove from drive queue        
  2434.             *pvHandle = (VolumeRecPtr)(pvPtr->nextVol);    // remove it from the linked list
  2435.             if (pvPtr == &gTheUTGlobals.theVolume)
  2436.             {
  2437.                 BlockZero((Ptr) &gTheUTGlobals.theVolume, sizeof(VolumeRec));
  2438.             }
  2439.             else
  2440.             {
  2441.                 PoolDeallocate( pvPtr );            // release its memory
  2442.             }
  2443.             drvRec->numVolumes--;                // decrement number of drive volumes
  2444.             break;
  2445.         }
  2446.         
  2447.         pvHandle = (VolumeRecPtr*)&(pvPtr->nextVol);        // save pointer to pv pointer
  2448.         pvPtr = (VolumeRecPtr)pvPtr->nextVol;                // point to next pv
  2449.     }            
  2450. }
  2451.  
  2452.  
  2453. //------------------------------------------------------------------------------
  2454. //    Function:        InstallDrive
  2455. //
  2456. //    Description:    Installs a physical drive and its volumes under the driver's
  2457. //                        control.  The driver determines if the drive is one it can
  2458. //                        manage, and if so, creates and initializes the drive's record,
  2459. //                        sets the drives operating mode and options, and mounts its
  2460. //                        partitions to the system.
  2461. //
  2462. //    Input:            drvNum:        physical drive reference
  2463. //                        mountVols:    true means to mount drive's partitions
  2464. //
  2465. //    Output:            true if successful, false if not
  2466. //-------------------------------------------------------------------------------
  2467. Boolean InstallDrive(DriveRecPtr theDrive, Boolean mountVols)
  2468. {
  2469.     // Initialize variables associated with the new drive.
  2470.     theDrive->numVolumes =        0;                            // no partitions yet
  2471.     
  2472.     //..............................................................................
  2473.     // Search for file system partitions on the media and install them as volumes
  2474.     // of this drive.  If no volumes, the drive must be considered unusable.
  2475.     InstallVolumes(theDrive, mountVols);
  2476.  
  2477.     if (theDrive->numVolumes)
  2478.     {
  2479.         OSErr theErr;
  2480.         
  2481.         theErr = MountVolumes( theDrive );
  2482.         if(theErr != noErr)
  2483.         {
  2484.             // For some reason, the disk could not be mounted,
  2485.             // We should eject and let the user decide whether to try again.
  2486.             gTheUTGlobals.currentExecutionState = kEjectStartState;
  2487.             EjectTheCartridge( &gTheUTGlobals );
  2488.         }
  2489.     }
  2490.     else                                                    // Abort if no volumes installed for drive        
  2491.     {
  2492.         return(false);
  2493.     }
  2494.  
  2495.     return(true);
  2496. }
  2497.  
  2498.  
  2499. //------------------------------------------------------------------------------
  2500. //    Function:        RemoveDrive
  2501. //    Description:    Removes a physical drive and its volumes from our control
  2502. //                    
  2503. //-------------------------------------------------------------------------------
  2504. void RemoveDrive(DriveRecPtr theDrivePtr)
  2505. {        
  2506.     VolumeRecPtr    vol;
  2507.     UInt16            vrefnum;
  2508.     
  2509.     if (theDrivePtr)                                // If the drive exists…
  2510.     {
  2511.         while (theDrivePtr->nextVol)                // dequeue all volumes on the drive…
  2512.         {
  2513.             vol = theDrivePtr->nextVol;                // get volume
  2514.             vrefnum = vol->vRefNum;                    // get its refnum
  2515.             RemoveVolume(theDrivePtr, vrefnum);        // delete volume from our queue
  2516.         }
  2517.     }
  2518. }
  2519.  
  2520.  
  2521. //------------------------------------------------------------------------------
  2522. //    Function:        NextQDrive
  2523. //
  2524. //    Description:    Returns the next unused logical drive number from the system 
  2525. //                    Drive Queue.  The Drive Queue is searched starting with a
  2526. //                    logical drive number 8.
  2527. //                    
  2528. //    Input:            none
  2529. //
  2530. //    Output:            The highest Drive Queue drive number + 1
  2531. //-------------------------------------------------------------------------------
  2532. SInt16 NextQDrive( void )
  2533. {
  2534.     QHdrPtr    qhdr = GetDrvQHdr();                // Pointer to Drive Queue 
  2535.     DrvQEl    *qel = (DrvQEl*) (qhdr->qHead);        // Pointer to first element 
  2536.     SInt16    drv = 8;                            // Start above built in drives 
  2537.  
  2538.     while (qel)                                 // While not end of queue, 
  2539.     {
  2540.         if (qel->dQDrive == drv)                 // if drive number used, 
  2541.         {
  2542.             drv++;                                // bump number, and 
  2543.             qel = (DrvQEl*) (qhdr->qHead);        // search from start 
  2544.         }
  2545.         else                                    // else next queue element 
  2546.             qel = (DrvQEl*) (qel->qLink);
  2547.     }
  2548.  
  2549.     return(drv);                        // Return the next logical drive 
  2550. }
  2551.  
  2552.  
  2553. //------------------------------------------------------------------------------
  2554. //    Function:        FindMountedVol
  2555. //    Description:    Searches the volume queue for a mounted volume specified 
  2556. //                    by vRefNum.  If one is found, its VCB pointer is returned.
  2557. //                    
  2558. //    Input:            vRefNum:    the volume reference to search for
  2559. //
  2560. //    Output:            the VCB pointer (NULL = volume not mounted)
  2561. //-------------------------------------------------------------------------------
  2562. static VCB *FindMountedVol(SInt16 vRefNum)
  2563. {
  2564.     QHdrPtr    volQ = GetVCBQHdr();        // VCB queue head pointer
  2565.     VCB        *theVol = (volQ) ? (VCB*)volQ->qHead : 0;    // first VCB
  2566.  
  2567.     while(theVol)
  2568.     {
  2569.         // The test for whether a volume is mounted or not is done using
  2570.         // the VCB fields vcbDrvNum and vcbDRefNum.  A volume is mounted
  2571.         // only if it is online.
  2572.         //
  2573.         //                    online            offline            ejected
  2574.         //
  2575.         //    vcbDrvNum        >0 (DrvNum)         0                 0
  2576.         //    vcbDRefNum        <0 (DRefNum)    <0 (-DrvNum)    >0 (DrvNum)
  2577.     
  2578.         if (theVol->vcbDrvNum == vRefNum)    // if volume specified is online…
  2579.             break;                            // volume is mounted
  2580.  
  2581.         theVol = (VCB*)theVol->qLink;        // next VCB
  2582.     }
  2583.     
  2584.     return(theVol);
  2585. }
  2586.  
  2587. //------------------------------------------------------------------------------
  2588. //    Function:        MountedVolOfDrive
  2589. //
  2590. //    Description:    Searches the system VCB for a mounted volume on the specified 
  2591. //                    physical drive.
  2592. //                    
  2593. //    Input:            drive:    pointer to the physical drive's record
  2594. //
  2595. //    Output:            the VCB pointer (nil if no volume mounted for drive)
  2596. //-------------------------------------------------------------------------------
  2597. VCB * MountedVolOfDrive(DriveRecPtr drive)
  2598. {
  2599.     VolumeRecPtr    vol = nil;
  2600.     VCB                *vcb = nil;
  2601.     
  2602.     if (drive)
  2603.     {
  2604.         vol = drive->nextVol;                // first volume (logical drive) of drive
  2605.         while (vol)
  2606.         {
  2607.             vcb = FindMountedVol(vol->vRefNum);        // check if a volume is mounted
  2608.             if (vcb)                                // if mounted volume, stop now
  2609.             {
  2610.                 break;
  2611.             }
  2612.             else                                                // next volume of drive
  2613.             {
  2614.                 vol = (VolumeRecPtr)vol->nextVol;
  2615.         }
  2616.     }
  2617.     }
  2618.     
  2619.     return(vcb);
  2620. }
  2621.  
  2622.  
  2623. //------------------------------------------------------------------------------
  2624. //    Function:        UpdateQ
  2625. //    Description:    Updates the specified drive in the system drive queue with
  2626. //                    the specified capacity
  2627. //                    
  2628. //    Input:            qDrive:        the drive to update
  2629. //                    newSize:    the new drive capacity
  2630. //-------------------------------------------------------------------------------
  2631. void UpdateQ(SInt16 qDrive, SInt32 newSize)
  2632. {
  2633.     QHdrPtr qhdr = GetDrvQHdr();                // Pointer to Drive Queue 
  2634.     DrvQEl *qel = (DrvQEl*) (qhdr->qHead);        // Pointer to first element 
  2635.  
  2636.     while (qel) 
  2637.     {
  2638.         // Search until drive is found, then update its capacity
  2639.         if (qel->dQDrive == qDrive)                // if drive number found, 
  2640.         {        
  2641.             qel->dQDrvSz2 = *(UInt16*)&newSize;    // new capacity (hi word)
  2642.             qel->dQDrvSz = newSize;                        // low word of capacity 
  2643.             break;
  2644.         }
  2645.         else                            // else next queue element 
  2646.             qel = (DrvQEl*) (qel->qLink);
  2647.     }
  2648. }
  2649.  
  2650. //------------------------------------------------------------------------------
  2651. //    FUNCTION:    NextPartitionID
  2652. //    PURPOSE:    Returns the next unique partition ID for all volumes associated
  2653. //                with the specified drive.  NOTE: This function should be used only
  2654. //                when adding volumes which do not have a partition map on the media.
  2655. //                
  2656. //    INPUT:        drive:    pointer to the drive record to search for next partition ID
  2657. //
  2658. //    OUTPUT:        a unique partition ID related to the specified volume
  2659. //
  2660. //-------------------------------------------------------------------------------
  2661. SInt32 NextPartitionID(DriveRecPtr drive)
  2662. {
  2663.     VolumeRecPtr vol = 0;
  2664.     SInt32 nextPartitionID = 1;                // Start search with partition ID of 1
  2665.  
  2666.     if (drive)                                // if drive is present…
  2667.     {
  2668.         vol = (VolumeRecPtr)drive->nextVol;            // first volume pointer
  2669.  
  2670.         while (vol)                            // while not end of volume queue…
  2671.         {
  2672.             if (vol->partitionNo == nextPartitionID)    // if partition ID used
  2673.             {
  2674.                 nextPartitionID++;                    // bump partition ID
  2675.                 vol = drive->nextVol;                // reset volume pointer
  2676.             }
  2677.             else
  2678.                 vol = (VolumeRecPtr)vol->nextVol;            // otherwise, point to next volume
  2679.         }
  2680.     }
  2681.     
  2682.     return(nextPartitionID);            // nil if volume not found
  2683. }
  2684.  
  2685.  
  2686. //------------------------------------------------------------------------------
  2687. //    Function:        MountVolumes
  2688. //    Description:    Mounts all volume for the specified drive.
  2689. //
  2690. //    Input:            DriveRecPtr drive:    drive with volumes to mount
  2691. //
  2692. //    Output:            Returns any errors that occur from PostEvent
  2693. //-------------------------------------------------------------------------------
  2694. OSErr MountVolumes( DriveRecPtr drive )
  2695. {
  2696.     VolumeRecPtr        vol;
  2697.     OSErr                mountErr = noErr;
  2698.                 
  2699.     // Post a Disk Inserted event for all HFS volumes not yet mounted
  2700.     if (drive)                                                        // for each drive…
  2701.     {
  2702.         vol = drive->nextVol;                                        // first volume structure
  2703.         while (vol)                                                 // for all volumes on drive…
  2704.         {                            
  2705.             if ((vol->driveStatus.diskInPlace != 0) &&                // if media in place,
  2706.                (vol->mountthispart) &&                                 // and volume to be mounted,
  2707.                (!vol->partmounted))                                    // and hasn't been done yet
  2708.             {
  2709.                 mountErr = PostEvent(diskEvt, vol->vRefNum);
  2710.  
  2711.                 if ( mountErr == noErr )
  2712.                 {
  2713.                     vol->partmounted = true;
  2714.                 }
  2715.             }
  2716.  
  2717.             vol = (VolumeRecPtr)vol->nextVol;                        // next per volume pointer
  2718.         }
  2719.     }
  2720.  
  2721.     return mountErr;                                                // return error if one occurred
  2722. }
  2723.  
  2724.  
  2725. // This is to workaround a bug in the PowerPC native version of the AddDrive
  2726. // call in systems before 8.5, where one needs to be added to the desired
  2727. // drive number before calling AddDrive.
  2728. // This function will check the system and pass the appropriate value to AddDrive
  2729. void NativeAddDrive(SInt16 drvrRefNum, UInt16 driveNumber, DrvQElPtr drvQEl)
  2730. {
  2731.     UInt32        gestaltResponse;
  2732.  
  2733.     // Check System version to see if we need to add one to AddDrive calls
  2734.     Gestalt    (gestaltSystemVersion,(long *) &gestaltResponse);
  2735.     if( (gestaltResponse&0xFFFF) < 0x0850 )
  2736.     {
  2737.         // We are on a system before 8.5, we need to add 1 to AddDrive calls
  2738.         driveNumber += 1;
  2739.     }
  2740.  
  2741.     AddDrive( drvrRefNum, driveNumber, drvQEl);
  2742. }
  2743.  
  2744.